题目:http://codeforces.com/contest/1009/problem/F
也可以用 dsu on tree 的做法,全局记录一个 dep,然后放进堆里,因为字典序要最小,所以再记一个第二关键字 dep[u];
长链剖分是 O(n) 的,因为如果 O(1) 继承重儿子(长儿子),对其他儿子枚举长度,那么每个点只会在向上第一次合并到重儿子时被枚举一次,所以总体 O(n);
然而不能开 f[1e6][1e6] 的数组,考虑到因为自己继承重儿子,所以数组有很大一部分是共用的,如果真的共用,数组总长度就是长链长度的和(每条长链只在顶端被开出来),也就是 n;
所以用指针,f[x] 指向数组 tmp 中的一个位置,对应 f[x][0] 。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=1e6+5; int n,hd[xn],ct,to[xn<<1],nxt[xn<<1],dep[xn],d[xn],son[xn],ans[xn]; int tmp[xn],*f[xn],*id=tmp; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} void dfs(int x,int fa) { dep[x]=d[x]=dep[fa]+1;//d[x]=dep[x] for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa)continue; dfs(u,x); if(d[u]>d[x])son[x]=u,d[x]=d[u]; } } void dfsx(int x,int fa) { f[x][0]=1; if(son[x])f[son[x]]=f[x]+1,dfsx(son[x],x),ans[x]=ans[son[x]]+1; for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa||u==son[x])continue; f[u]=id; id+=d[u]-dep[u]+1; dfsx(u,x); for(int j=0;j<=d[u]-dep[u];j++) { f[x][j+1]+=f[u][j]; if(f[x][j+1]>f[x][ans[x]]||(f[x][j+1]==f[x][ans[x]]&&j+1<ans[x])) ans[x]=j+1;//f[x][ans[x]]!! } } if(f[x][ans[x]]==1)ans[x]=0;//cnt=1 } int main() { n=rd(); for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x); dfs(1,0); f[1]=id; id+=d[1]; dfsx(1,0);//f[1] for(int i=1;i<=n;i++)printf("%d ",ans[i]); return 0; }