[loj3014]独特的城市
注意到与$u$独特的点总是在距离$u$最远的点上,而直径的端点总包含距离$u$最远的点
换言之,设直径为$(a,b)$,则以$a,b$为根分别求出$u$到根路径上与$u$独特的点即可(仅在$u$较深的树上查询)
关于上述过程,可以维护一个栈,在递归$u$时依次执行以下过程:
1.弹出栈顶直至栈顶到$u$距离$>cmx$,在栈中加入$u$并递归$u$的长儿子
2.弹出栈顶直至栈顶到$u$距离$>mx$,并递归其余儿子(递归前若栈顶不为$u$则加入$u$)
3.(若栈顶为$u$)弹出栈顶的$u$,此时栈中的元素即为与$u$独特的点
(其中$cmx$为$u$轻儿子子树内距离$u$最远的点,$mx$为$u$子树内距离$u$最远的点)
关于正确性,注意在递归完$u$后栈并不需要还原,具体原因易得
关于复杂度,注意到$u$至多被插入$\deg(u)$次,因此均摊总复杂度为$o(n)$
在本问题中,还需要在栈变化时用一个桶维护其中特产数
时间复杂度为$o(n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 struct Edge{ 5 int nex,to; 6 }edge[N<<1]; 7 stack<int>st; 8 int E,n,m,x,y,a[N],head[N],dep[N],l[N],cl[N],mx[N],tot[N],ans[N]; 9 void add(int x,int y){ 10 edge[E].nex=head[x]; 11 edge[E].to=y; 12 head[x]=E++; 13 } 14 void dfs(int k,int fa,int s){ 15 dep[k]=s; 16 mx[k]=l[k]=cl[k]=0; 17 for(int i=head[k];i!=-1;i=edge[i].nex) 18 if (edge[i].to!=fa){ 19 dfs(edge[i].to,k,s+1); 20 int x=l[edge[i].to]+1; 21 if (l[k]<x){ 22 mx[k]=edge[i].to; 23 swap(l[k],x); 24 } 25 cl[k]=max(cl[k],x); 26 } 27 } 28 void add(int k){ 29 st.push(k); 30 if (++tot[a[k]]==1)ans[0]++; 31 } 32 void del(){ 33 if (--tot[a[st.top()]]==0)ans[0]--; 34 st.pop(); 35 } 36 void calc(int k,int fa){ 37 if (fa)add(fa); 38 while ((!st.empty())&&(dep[k]-dep[st.top()]<=cl[k]))del(); 39 if (mx[k])calc(mx[k],k); 40 while ((!st.empty())&&(dep[k]-dep[st.top()]<=l[k]))del(); 41 ans[k]=max(ans[k],ans[0]); 42 for(int i=head[k];i!=-1;i=edge[i].nex) 43 if ((edge[i].to!=fa)&&(edge[i].to!=mx[k]))calc(edge[i].to,k); 44 if ((!st.empty())&&(st.top()==fa))del(); 45 } 46 int main(){ 47 scanf("%d%d",&n,&m); 48 memset(head,-1,sizeof(head)); 49 for(int i=1;i<n;i++){ 50 scanf("%d%d",&x,&y); 51 add(x,y); 52 add(y,x); 53 } 54 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 55 dfs(1,0,0); 56 x=1; 57 for(int i=2;i<=n;i++) 58 if (dep[x]<dep[i])x=i; 59 dfs(x,0,0); 60 calc(x,0); 61 x=1; 62 for(int i=2;i<=n;i++) 63 if (dep[x]<dep[i])x=i; 64 dfs(x,0,0); 65 calc(x,0); 66 for(int i=1;i<=n;i++)printf("%d\n",ans[i]); 67 }