CodeForces 741D....path【树上启发式合并】
题意
一棵根为1的树,每条边上有一个字符(a-v共22种)。 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的Dokhtar-kosh路径的长度。
题解
既然要重新排列,那么这个路径的条件就是:
- 简单路径
- 路径中 22 种字母最多只能由一种字母数量为奇数。
如果我一直从某点到根节点的字母的情况,那么对于另一个点,如果在所有字母中奇偶性与之相同或者只有1种奇偶性不同,那么这两个点之间的简单路径就是Dokhtar-kosh。
然后这样的统计可以使用树上启发式合并,方法和什么点分治差不多。
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <queue>
#define xx first
#define yy second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int inf=0x3f3f3f3f;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=5e5+10;
const int M=1e6+10;
int n,sta[N],dep[N],siz[N],son[N];
int ans[N],maxlen[1<<22],rt,node[N],cnt;
vector<PII> g[N];
void predfs(int u,int fa,int state){
siz[u]=1;sta[u]=state;dep[u]=dep[fa]+1;
for(PII e:g[u]){
int v=e.xx,c=e.yy;
if(v==fa) continue;
predfs(v,u,state^(1<<c));
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void getnode(int u,int fa){
node[++cnt]=u;
for(PII e:g[u]) if(e.xx!=fa) getnode(e.xx,u);
}
void clear(int u,int fa){
maxlen[sta[u]]=-inf;
for(PII e:g[u]) if(e.xx!=fa) clear(e.xx,u);
}
void calc(int u){
ans[rt]=max(ans[rt],maxlen[sta[u]]+dep[u]-2*dep[rt]);
for(int i=0;i<22;i++)
ans[rt]=max(ans[rt],maxlen[sta[u]^(1<<i)]+dep[u]-2*dep[rt]);
}
void dfs(int u,int fa,bool keep){
for(PII e:g[u]){
if(e.xx==fa||e.xx==son[u]) continue;
dfs(e.xx,u,false);
}
if(son[u]) dfs(son[u],u,true);
rt=u;
calc(u);
maxlen[sta[u]]=max(maxlen[sta[u]],dep[u]);
for(PII e:g[u]){
if(e.xx==fa||e.xx==son[u]) continue;
cnt=0;
getnode(e.xx,u);
for(int i=1;i<=cnt;i++) calc(node[i]);
for(int i=1;i<=cnt;i++) maxlen[sta[node[i]]]=max(maxlen[sta[node[i]]],dep[node[i]]);
}
for(PII e:g[u]) if(e.xx!=fa) ans[u]=max(ans[u],ans[e.xx]);
if(!keep) clear(u,fa);
}
int main(){
memset(maxlen,0xc0,sizeof(maxlen));
scanf("%d",&n);
for(int v=2,u;v<=n;v++){
char s[10];
scanf("%d%s",&u,s);
g[u].push_back({v,s[0]-'a'});
g[v].push_back({u,s[0]-'a'});
}
predfs(1,0,0);
dfs(1,0,true);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}