【GDOI2017 day1】取石子游戏

Description

题意简单一点:给出一棵树,让你求出每个节点的子树以外的mex(最小没出现过的自然数)

Solution

比赛的时候没有看懂,就没敢去打,其实思想很简单。
对于一个序列来说,包含一个x的区间答案肯定不是x。
那么我们把这个想法放到树上。
对于颜色x,我们提取出来,求出他们的lca(求lca是满足结合律的),假设这个lca为y,那么很显然的除了y这个子树,其他的子树都没有x这个颜色了。现在我们要求的是子树之外的mex,那么包含y这个子树的只有y到根路径上的节点。
那么我们现在从小到大枚举颜色,求出lca后,一个个往上跳,假如当前这个节点已经被标记过颜色了,那么就可以break掉,否则就标色。因为从小到大,肯定满足最小。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=1e6+7;
int i,j,k,l,t,n,m,ans,cas,x,y;
int first[maxn],last[maxn],next[maxn],a[maxn],num;
int f[maxn][21],deep[maxn];
int bz[maxn],az[maxn],hou[maxn],an[maxn],du[maxn],d[maxn];
int get(){
    char ch=getchar();int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
void add(int x,int y){
    last[++num]=y,next[num]=first[x],first[x]=num;
}
void bfs(){
    int i,head=0,tail=0,x;
    fo(i,1,n)if(!du[i])d[++tail]=i;deep[1]=1;
    while(headif(!du[last[i]])d[++tail]=last[i];
            f[last[i]][0]=x;deep[last[i]]=deep[x]+1;
        }
    }
}
int lca(int x,int y){
    int i;if(deep[x]20,0)if(deep[f[x][i]]>deep[y])x=f[x][i];
    if(deep[x]!=deep[y])x=f[x][0];
    fod(i,20,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    if(x!=y)return f[x][0];return x;
}
int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    for(scanf("%d",&cas);cas;cas--){
        num=0,memset(first,0,sizeof(first));memset(bz,0,sizeof(bz));
        memset(an,255,sizeof(an));memset(du,0,sizeof(du));
        scanf("%d%d",&n,&m);
        fo(i,1,n)a[i]=get();
        fod(i,n,1){
            hou[i]=bz[a[i]];bz[a[i]]=i;
        }
        fo(i,1,n-1)k=get(),l=get(),add(k,l),du[l]++;
        bfs();fo(j,1,20)fo(i,1,n)f[i][j]=f[f[i][j-1]][j-1];
        fo(i,0,m){
            x=bz[i];y=0;
            if(!x){
                fo(j,1,n)if(an[j]==-1)an[j]=i;break;
            }
            while(x){
                if(!y)y=x;
                else y=lca(y,x);
                x=hou[x];
            }
            while(y&&an[y]==-1)an[y]=i,y=f[y][0];
        }
        fo(i,1,n){if(an[i]==-1)an[i]=0;printf("%d ",an[i]);}
        printf("\n");
    }
}

你可能感兴趣的:(树,省选,暴搜)