题解:P6118 [JOI 2019 Final] 独特的城市 / Unique Cities
建议食用:我的博客
看题解没看太懂,遂写一篇细说一下在信息转移中的感悟。
一个结论
一个点
一个做法
考虑分别以
为什么我们要先遍历长儿子再遍历其他?
(在该图中,我们遍历到节点
考虑到对儿子的影响,我们不能直接删掉所有
考虑到这些路径与根节点的深度重合节点其实本质上只需要找出这些路径中的最长链,然后对祖先路径进行去重即可。明显我们要除去的
这样我们就得到了一个
- 加入父节点。
- 删掉祖先路径上距离
\le 次长链的节点。 - 遍历长儿子。
- 删掉祖先路径上距离
\le 最长链的节点。 - 统计答案
- 遍历其他儿子。
- 删掉父节点。
具体实现如下:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,c[N],ans[N];
vector<int>G[N];
int st,en,dep[N];
int mxdep[N],cdep[N],son[N];
void dfs0(int u,int fa){
dep[u]=dep[fa]+1;
if(dep[u]>dep[en])en=u;
for(int v:G[u]){
if(v==fa)continue;
dfs0(v,u);
}
}
void merge(int x,int y){
if(mxdep[y]>mxdep[x])cdep[x]=mxdep[x],mxdep[x]=mxdep[y],son[x]=y;
else if(mxdep[y]>cdep[x])cdep[x]=mxdep[y];
}
void dfs1(int u,int fa){
dep[u]=dep[fa]+1;
mxdep[u]=cdep[u]=0;
son[u]=0;
for(int v:G[u]){
if(v==fa)continue;
dfs1(v,u);
merge(u,v);
}
if(!son[u])mxdep[u]=dep[u];
}
int stk[N],tp,buk[N],ext;
void add(int &u){
buk[c[u]]++;
if(buk[c[u]]==1)ext++;
}
void del(int &u){
buk[c[u]]--;
if(!buk[c[u]])ext--;
}
void dfs3(int u,int fa){
if(fa)add(stk[++tp]=fa);
while(tp&&dep[u]-dep[stk[tp]]<=cdep[u]-dep[u])del(stk[tp--]);
if(son[u])dfs3(son[u],u);
while(tp&&dep[u]-dep[stk[tp]]<=mxdep[u]-dep[u])del(stk[tp--]);
ans[u]=max(ans[u],ext);
for(int v:G[u]){
if(v==fa||v==son[u])continue;
dfs3(v,u);
}
if(tp&&stk[tp]==fa)del(stk[tp--]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
st=1,en=0;dfs0(st,0);
st=en,en=0;dfs0(st,0);
dfs1(st,0);dfs3(st,0);
dfs1(en,0);dfs3(en,0);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}