CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
Link
树上启发式合并(静态链分治)模板题。
首先一个串能重排形成是回文串当且仅当其字符数量最多有一个为奇数。
因此我们只关心路径上某个字符的奇偶性。
所以我们对字符集状压,\(0\)表示偶,\(1\)表示奇。
记\(dis_u\)为\(1\)到\(u\)的路径上的字符集。
那么对于点对\(u,v\),其路径上的字符集为\(dis_u\oplus dis_v\)。
一条路径的字符集\(S\)合法当且仅当\(S=2^i(i\in[0,21])\bigvee S=0\)。
我们先考虑暴力的做法:
开一个桶\(t_i\)记录\(dis_u\)为\(i\)的点中\(dep\)最大的点\(u\)。
对于点\(x\),以任意顺序遍历其子树,然后对于子树中的点\(u\),检查桶内与\(u\)路径合法的最大\(dep\)的点\(v\),那么贡献就为\(dep_u+dep_v-2dep_{lca}\)。
然后考虑静态链分治。
静态链分治是解决只有对子树的询问且没有修改的问题的算法。
我们先轻重链剖分。
第一步,递归计算所有轻儿子,并且将轻儿子子树内的贡献加到当前节点,计算完之后清空影响。
第二步,递归计算所有重儿子,并且将重儿子子树内的贡献加到当前节点,计算完之后不清空影响。
第三步,遍历其所有轻儿子的子树,根据重儿子的影响来计算跨越重儿子与轻儿子的贡献。
记得计算的同时要更新影响。
同时为了能在不递归的情况下遍历轻儿子的子树,我们需要记一个欧拉序。
对于某些问题比如路径问题而言,我们也需要注意一下路径一段在当前点的贡献。
再以这题为例讲一下具体做法。
第一步,递归计算所有轻儿子中的路径,当前节点的答案对各个轻儿子的答案取max,计算完之后清空桶。
第二步,递归计算所有重儿子中的路径,当前节点的答案对各个轻儿子的答案取max,计算完之后不清空桶。
第三步,根据重儿子的桶计算路径一端在当前点另一端在重儿子子树中的答案。
第四步,遍历其所有轻儿子子树中的点,根据重桶计算路径一端在当前点另一端在轻儿子子树和路径两端都在轻儿子子树的答案。
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
char fetch(){char c=Get();while(!islower(c))c=Get();return c;}
int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put(' ');}
}
using namespace IO;
void max(int &a,int b){a=a>b? a:b;}
const int N=500007;
vector<int>G[N],W[N];
int L[N],R[N],dep[N],size[N],son[N],dis[N],id[N],vis[N],ans[N],t[5000000],T,n;
void dfs(int u)
{
size[u]=1,L[u]=++T,id[T]=u;
for(int i=0,v;i<G[u].size();++i) dis[v=G[u][i]]=dis[u]^W[u][i],dep[v]=dep[u]+1,dfs(v),size[u]+=size[v],son[u]=size[v]>size[son[u]]? v:son[u];
R[u]=T;
}
void dfs2(int u,int f)
{
int i,j,k,v,x,y;
for(i=0;i<G[u].size();++i) if((v=G[u][i])^son[u]) dfs2(v,0),max(ans[u],ans[v]);
if(son[u]) dfs2(son[u],1),max(ans[u],ans[son[u]]);
if(t[dis[u]]) max(ans[u],t[dis[u]]-dep[u]);
for(i=0;i<22;++i) if(t[x=(dis[u]^1<<i)]) max(ans[u],t[x]-dep[u]);
max(t[dis[u]],dep[u]);
for(i=0;i<G[u].size();++i)
{
if((v=G[u][i])==son[u]) continue;
for(j=L[v];j<=R[v];++j)
{
if(t[dis[x=id[j]]]) max(ans[u],t[dis[x]]+dep[x]-(dep[u]<<1));
for(k=0;k<22;++k) if(t[y=(dis[x]^1<<k)]) max(ans[u],t[y]+dep[x]-(dep[u]<<1));
}
for(j=L[v];j<=R[v];++j) max(t[dis[id[j]]],dep[id[j]]);
}
if(!f) for(i=L[u];i<=R[u];++i) t[dis[id[i]]]=0;
}
int main()
{
int i,x;char c; n=read();
for(i=2;i<=n;++i) x=read(),c=fetch(),G[x].pb(i),W[x].pb(1<<c-97);
dfs(dep[1]=1),dfs2(1,1);
for(i=1;i<=n;++i) write(ans[i]);
return Flush(),0;
}