II.II.CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
名字真长
假如它没有”在每个子树中最长“的限制,我们倒真可以点分治,然后就是水题了;但是它要求在每个子树中最长,怎么办呢?
直接上dsu on tree就行了。思想还是借鉴点分治,所有点对都在LCA处处理就行。先处理轻儿子子树并回撤,然后处理重儿子不回撤,然后加入自身及轻儿子影响。在加入一坨东西时,要先查看桶中是否有能与其配对成回文串的另一半(明显一共只有 \(23\) 种),然后再加,这是避免子树内部配对。都是点分治的老套路了,不必多说。
所以dsu on tree似乎能完美替代大部分点分治的题?
代码:
#include<bits/stdc++.h>
using namespace std;
int n,msk[500100],fa[500100],son[500100],sz[500100],dep[500100],buc[1<<22],res[500100],ans;
char s[10];
vector<int>v[500100];
void dfs1(int x){
sz[x]=1;
for(auto y:v[x]){
dep[y]=dep[x]+1,dfs1(y),sz[x]+=sz[y];
if(sz[son[x]]<sz[y])son[x]=y;
}
}
void dfsread(int x){
if(buc[msk[x]]!=-1)ans=max(ans,dep[x]+buc[msk[x]]);
for(int i=0;i<22;i++)if(buc[msk[x]^(1<<i)]!=-1)ans=max(ans,dep[x]+buc[msk[x]^(1<<i)]);
for(auto y:v[x])dfsread(y);
}
void dfswrite(int x){
buc[msk[x]]=max(buc[msk[x]],dep[x]);
for(auto y:v[x])dfswrite(y);
}
void dfserase(int x){
buc[msk[x]]=-1;
for(auto y:v[x])dfserase(y);
}
void dsuontree(int x){
for(auto y:v[x])if(y!=son[x])dsuontree(y),dfserase(y);
if(son[x])dsuontree(son[x]);
ans=0;
buc[msk[x]]=max(buc[msk[x]],dep[x]);
if(buc[msk[x]]!=-1)ans=max(ans,dep[x]+buc[msk[x]]);
for(int i=0;i<22;i++)if(buc[msk[x]^(1<<i)]!=-1)ans=max(ans,dep[x]+buc[msk[x]^(1<<i)]);
for(auto y:v[x])if(y!=son[x])dfsread(y),dfswrite(y);
res[x]=ans-2*dep[x];
for(auto y:v[x])res[x]=max(res[x],res[y]);
}
int main(){
scanf("%d",&n),memset(buc,-1,sizeof(buc));
for(int i=2;i<=n;i++)scanf("%d%s",&fa[i],s),msk[i]=msk[fa[i]]^(1<<(s[0]-'a')),v[fa[i]].push_back(i);
dfs1(1);
dsuontree(1);
for(int i=1;i<=n;i++)printf("%d ",res[i]);
return 0;
}