2018 8.8 提高A组模拟赛

T1
Description

被污染的灰灰草原上有羊和狼。有N只动物围成一圈,每只动物是羊或狼。
该游戏从其中的一只动物开始,报出[1,K]区间的整数,若上一只动物报出的数是x,下一只动物可以报[x+1,x+K]区间的整数,游戏按顺时针方向进行。每只动物报的数字都不能超过M。若一只动物报了M这个数,它所在的种族就输了。问以第i只动物为游戏的开始,最后哪种动物会赢?

Input

第一行输入三个正整数N,M,K。
接下来一行N个正整数,分别表示N只动物的种类,以顺时针的方向给出。0代表羊,1代表狼。

Output

一行输出N个整数,表示若从第i只动物开始,赢的动物的种类。同上,0代表羊,1代表狼。

Sample Input

Input 1
2 9 2
0 1
Input 2
6 499 5
1 0 0 1 1 0
Input 3
10 100 10
0 0 0 1 1 1 1 0 1 1

Sample Output

Output 1
0 1
Output 2
0 1 1 1 1 0
Output 3
1 1 1 1 1 1 1 1 1 1

Data Constraint

对于60%的数据,1 ≤ N, M, K ≤ 500。
对于100%的数据,1 ≤ N, M, K ≤ 5000。

解题思路

dp+博弈论,dp[i][j]表示 轮i报数报到j有无必胜策略,这样是n^3的,需要前缀和优化,如果前缀和=0,说明此时状态为必败。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>

using namespace std;
const int MAXN = 5005;

int dp[MAXN<<1][MAXN];
int n,m,K,sum,a[MAXN];

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

inline int pre(int x){
    return x%n==0?n:x%n; 
}

int main(){
    freopen("vode.in","r",stdin);
    freopen("vode.out","w",stdout);
    n=rd();m=rd();K=rd();
    for(register int i=1;i<=n;i++) a[i]=rd();
//  for(register int i=1;i<=n+m;i++) 
//      for(register int j=1;j<=m;j++) dp[i][j]=1;
//  for(register int i=1;i<=n+m;i++) dp[i][m]=0;
    for(register int i=n+m-1;i;i--){
        sum=0;
        for(register int j=m-1;j;j--){
            if(a[pre(i)]==a[pre(i+1)]) dp[i][j]=(sum>0);
            else dp[i][j]=(sum==0);
            sum+=dp[i+1][j];
            if(j+K<=m) sum-=dp[i+1][j+K]; 
        }
    }
    for(register int i=1;i<=n;i++){
        int flag=0;
        for(register int j=1;j<=K;j++)
            if(dp[i][j]) {flag=1;break;}
        printf("%d ",flag?a[i]:a[i]^1);
    }
    return 0;
}

T2
Description

有一副n*m的地图,有n*m块地,每块是下列四种中的一种:
墙:用#表示,墙有4个面,分别是前面,后面,左面,右面。
起点:用C表示,为主角的起点,是一片空地。
终点:用F表示,为主角的目的地,是一片空地。
空地:用 . 表示。
其中除了墙不能穿过,其他地方都能走。

主角有以下3种操作:
1.移动到相邻的前后左右的地方,花费一个单位时间。
2.向前后左右其中一个方向发射子弹,子弹沿直线穿过,打在最近的一堵墙的一面,然后墙的这面就会形成一个开口通往秘密通道。同一时间最多只能有两个开口,若出现有3个开口,出现时间最早的开口会立即消失。该操作不用时间。
3.可以从一个与开口相邻的空地跳进去,进入秘密通道,从另外一个开口正对的空地跳出来。这个过程花费一个单位时间。

地图四周都是墙,问主角最少用多少时间从C走到F。C和F
只会出现一次。

Input

第一行输入两个正整数n,m。
接下来n行,每行m个字符描述地图。

Output

输出1个整数,表示最短时间完成路途。如果无解输出nemoguce

Sample Input

Input 1
4 4

#

.F

C.

#

Input 2
6 8

#

.##..F

C.##..

..#…

…..

#

Input 3
4 5

#

C#.

F

#

Sample Output

Output 1
2
Output 2
4
Output 3
nemoguce

Data Constraint

对于50%的数据,4≤ n,m≤ 15。
对于100%的数据,4≤ n,m≤ 500。

Hint

总共用到8次操作,时间之和为4。如下图所示
1.向左射一枪,在(3,1)的右面出现开口。
2.向下射一枪,在(6,2)的上面出现开口。
3.向左从(3,1)进入秘密通道,从(6,2)中出来,到达(5,2)。用1单位时间。
4.向右射一枪,在(5,7)的左面出现开口,(3,1)右面的开口消失。
5.走进(6,2)的开口,出来到(5,6)。用1单位时间。
6.向上射一枪,在(1,6)的下面出现开口。
7.经过秘密通道,走到(2,6)。用1单位时间。
8.走到终点。用1单位时间。

解题思路

对于每个非墙的点,向它四周连一条边权为1的边,还要向它四周的墙连一条据他最近的墙的边,然后跑一边最短路即为答案。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>

using namespace std;
const int MAXN = 502;
const int MAXM = MAXN*MAXN;
const int inf = 0x3f3f3f3f;

int n,m,head[MAXM],cnt,S,T,dis[MAXM];
int to[MAXM<<3],nxt[MAXM<<3],val[MAXM<<3];
char c[MAXN][MAXN];
bool vis[MAXM];
queue<int> Q;

inline void add(int bg,int ed,int w){
    to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt,val[cnt]=w;
}

inline void spfa(){
    memset(dis,0x3f,sizeof(dis));
    dis[S]=0;vis[S]=1;Q.push(S);
    while(!Q.empty()){
        int x=Q.front();Q.pop();vis[x]=0;
        for(register int i=head[x];i;i=nxt[i]){
            int u=to[i];
            if(dis[x]+val[i]<dis[u]){
                dis[u]=dis[x]+val[i];
                if(!vis[u]){
                    vis[u]=1;
                    Q.push(u);
                }
            }
        }
    }
}

inline int minn(int x,int y){
    if(x>y) return y;
    return x;
}

int main(){
    freopen("portal.in","r",stdin);
    freopen("portal.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++) scanf("%s",c[i]+1);
    for(register int i=2;i<n;i++)
        for(register int j=2;j<m;j++){
            if(c[i][j]=='C') S=(i-1)*m+j;
            if(c[i][j]=='F') T=(i-1)*m+j;
            if(c[i][j]!='#') {
                if(c[i+1][j]!='#') add((i-1)*m+j,i*m+j,1),add(i*m+j,(i-1)*m+j,1);
                if(c[i][j+1]!='#') add((i-1)*m+j,(i-1)*m+j+1,1),add((i-1)*m+j+1,(i-1)*m+j,1);
            }
        }
    for(register int i=2;i<n;i++)
    for(register int j=2;j<m;j++) if(c[i][j]!='#') {
            int u,d,l,r;int zz=(i-1)*m;
            int xx=i,yy=j;int w=1e9;
            while(c[xx][yy+1]!='#') yy++;
            w=minn(w,yy-j);u=yy;yy=j;
            while(c[xx][yy-1]!='#') yy--;
            w=minn(w,j-yy);d=yy;yy=j;
            while(c[xx+1][yy]!='#') xx++;
            w=minn(w,xx-i);r=xx;xx=i;
            while(c[xx-1][yy]!='#') xx--;
            w=minn(w,i-xx);l=xx;xx=i;w++;
            if(u!=yy) add(zz+j,zz+u,w);
            if(d!=yy) add(zz+j,zz+d,w);
            if(r!=xx) add(zz+j,(r-1)*m+j,w);
            if(l!=xx) add(zz+j,(l-1)*m+j,w);
        }
    spfa();
    if(dis[T]!=inf) printf("%d",dis[T]);
    else puts("nemoguce");
}

T3

Input

第一行输入三个正整数n,m,q,其中q表示询问个数。
接下来q行,每行两个正整数x,y,表示询问城市x和城市y最早什么时候连通。

Output

输出q行,每行一个正整数,表示最早连通的天数

Sample Input

Input 1
8 3 3
2 5
3 6
4 8
Input 2
25 6 1
20 9
Input 3
9999 2222 2
1025 2405
3154 8949

Sample Output

Output 1
3
1
2
Output 2
4
Output 3
1980
2160

Data Constraint

对于40%的数据,n≤ 1000,q<=100000
对于100%的数据,1 ≤ n,q≤ 100000,1<=m<=q

解题思路

发现每天所连的边为调和级数,不超过nln n 级别的,而树高不超过log n。所以用按秩合并的权值并查集合并,然后每次暴力求lca,在两个点的链上求最小值。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>

using namespace std;
const int MAXN = 100005;

int n,m,q,siz[MAXN],val[MAXN];
int fa[MAXN],ans;
set<int> S;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

inline int fd(int x){
    if(x==fa[x]) return x;
    return fd(fa[x]);
}

inline void update(int x,int y,int t){
    int u=fd(x),v=fd(y);
    if(u==v) return;
    if(siz[u]>siz[v]){
        val[v]=t;fa[v]=u;
        siz[u]+=siz[v];
    }
    else{
        val[u]=t;fa[u]=v;
        siz[v]+=siz[u];
    }
}

int main(){
    freopen("pictionary.in","r",stdin);
    freopen("pictionary.out","w",stdout);
    n=rd();m=rd();q=rd();
    for(register int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(register int i=1;i<=m;i++){
        int now=m-i+1;
        for(register int j=now*2;j<=n;j+=now) update(now,j,i);
    }
    for(register int i=1;i<=q;i++){
        int x=rd(),y=rd();int xx=x;
        ans=0;S.clear();
        while(x!=fa[x]) S.insert(x),x=fa[x];
        while(y!=fa[y] && S.find(y)==S.end()) ans=max(ans,val[y]),y=fa[y];
        while(xx!=y) ans=max(ans,val[xx]),xx=fa[xx];
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2018-08-09 20:29  Monster_Qi  阅读(112)  评论(0编辑  收藏  举报