[CF741D]Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(DSU on tree)
题面
https://codeforces.com/contest/741/problem/D
题解
前置知识:
- DSU on tree:https://oi-wiki.org/graph/dsu-on-tree/
首先,对题目中”一条路径上的字符,重排后能够构成回文串“这个条件做一下转化。
- 如果路径长是偶数,那么等价于:在这条路径上,所有字符出现的个数都是偶数。
- 如果路径长是奇数,那么等价于:在这条路径上,有且仅有一种字符出现的个数是奇数。
因此,我们可以对于每一个树上节点u,维护从u到根的路径上,各字符出现的个数的奇偶性。存储可以二进制压位。设这个值为\(mask[u]\)。
那么一条路径\(path(u,v)\)是合法的,当且仅当\(pop{\_}cnt(mask[u] \bigoplus mask[v]) \leq 1\)(\(pop{\_}cnt[u]\)表示u在二进制表示下的1的个数)。它只有23种选择。
考虑统计经过某一个点u,所有合法串的最大值ans[u]。可以使用DSU on tree来做。
具体地说,维护f[x]表示当前所有mask[u]=x的u的深度最大值。在dfs到u点时,f中存储的将是u重儿子的信息。然后,逐一遍历u每一个轻儿子v,对于v子树中的所有点更新ans[u],再更新f。再对点u本身更新ans[u]和f。之后如果u不是fa[u]的重儿子则清空f。整个处理方法类似于点分治。
过程中,“对于v更新ans[u]”指的是,需要找到能够与v形成合法路径的w的深度的最大值。而我们知道了mask[v],就确定了mask[w]的全部23种可能性,只需去f中查找,并取最大值得到cur。然后使ans[u]与\(cur+dep[v]-2 \times dep[u]\)取较大值。
总时间复杂度\(O(|C|n \log n)\)。其中\(|C|\)为字符集大小,本题中为22。
代码
#include<bits/stdc++.h>
using namespace std;
#define rg register
#define In inline
const int N = 5e5;
const int M = 1 << 22;
const int inf = 0x3f3f3f3f;
namespace IO{
In int read(){
int s = 0,ww = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
return s * ww;
}
In void write(int x){
if(x < 0)putchar('-'),x = -x;
if(x > 9)write(x / 10);
putchar('0' + x % 10);
}
}
using namespace IO;
int f[M+5],ans[N+5];
int mask[N+5];
struct edge{
int next,des,ch;
}e[N+5];
int cnt;
int head[N+5];
In void addedge(int a,int b,char ch){
cnt++;
e[cnt].des = b;
e[cnt].next = head[a];
e[cnt].ch = ch - 'a';
head[a] = cnt;
}
int son[N+5],sz[N+5],dfn[N+5],D[N+5],dep[N+5];
int dn;
void prepro(int u,int msk){
mask[u] = msk;
dn++;
D[dn] = u,dfn[u] = dn;
sz[u] = 1;
int maxn = 0;
for(rg int i = head[u];i;i = e[i].next){
int v = e[i].des;
dep[v] = dep[u] + 1;
prepro(v,msk ^ (1<<e[i].ch));
sz[u] += sz[v];
if(sz[v] > maxn)maxn = sz[v],son[u] = v;
}
}
In int query(int cur){
int rt = f[cur];
for(rg int i = 0;i < 22;i++)rt = max(rt,f[cur^(1<<i)]);
return rt;
}
void dfs(int u,int keep){
for(rg int i = head[u];i;i = e[i].next){
int v = e[i].des;
if(v == son[u])continue;
dfs(v,0);
}
if(son[u])dfs(son[u],1);
for(rg int i = head[u];i;i = e[i].next){
int v = e[i].des;
if(v == son[u])continue;
for(rg int j = dfn[v];j <= dfn[v] + sz[v] - 1;j++){
int cur = mask[D[j]];
ans[u] = max(ans[u],query(cur) + dep[D[j]] - 2 * dep[u]);
}
for(rg int j = dfn[v];j < dfn[v] + sz[v];j++)f[mask[D[j]]] = max(f[mask[D[j]]],dep[D[j]]);
}
ans[u] = max(ans[u],query(mask[u]) - dep[u]);
f[mask[u]] = max(f[mask[u]],dep[u]);
for(rg int i = head[u];i;i = e[i].next)ans[u] = max(ans[u],ans[e[i].des]);
if(!keep){
for(rg int i = dfn[u];i <= dfn[u] + sz[u] - 1;i++)
f[mask[D[i]]] = -inf;
}
}
int main(){
// freopen("CF741D.in","r",stdin);
// freopen("CF741D.out","w",stdout);
int n = read();
for(rg int i = 0;i < (1 << 22);i++)f[i] = -inf;
for(rg int i = 2;i <= n;i++){
int f = read();
char ch = getchar();
addedge(f,i,ch);
}
prepro(1,0);
dfs(1,1);
for(rg int i = 1;i <= n;i++){
write(ans[i]);
putchar(i == n ? '\n' : ' ');
}
return 0;
}