Description

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

Solution

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

Code

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<string.h>
#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(head<tail){
x=d[++head];
rep(i,x){
du[last[i]]--;if(!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]<deep[y])swap(x,y);
fod(i,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");
}
}