CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths相关的知识,希望对你有一定的参考价值。
CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
题意:
一棵根为1 的树,每条边上有一个字符(a-v共22种)。 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的Dokhtar-kosh路径的长度。
题解:
树上启发式合并的开山之作
树上启发式合并介绍
详细题解
字符重排后为字符串,说明该字符中最多只有一个字符出现奇数次,其余都是偶数次,我们联想到用异或操作
我们将字符都用二进制来表示,比如a是0001,b是0010,以此类推,我们可以根据一条路上的异或和来判断是否为回文串
树上两条路径之间的异或和:
1为根节点
dis(u,v) = dis(u, 1 ) ^ dis(lca(u,v) 1) ^ dis(v,1) ^ dis(lca(u,v) ,1) = dis(u,1) ^ dis(v,1)
所以我们只需要处理出每个点到根节点1的路径异或和Xor(u)=dis(u,1),就可以处理任意两点之间的异或和
当一个路径合法时,当且仅当Xor(u) ^ Xor(v)的二进制数中有且仅有一个1(也就是最多就一个字符为奇数)或者没有1
即合法路径(u,v) 为Xor[u] ^ Xor[v] =0 或者Xor[u] ^ Xor[v] = 2i
我们需要求的就是以 u 为根子树中的两点 x,y,满足 Xor[x] ^ Xor[y] =0 或者Xor[x] ^ Xor[y] = 2i 的点对所有 (x,y)中的最长长度
对于以u为根的子树,树中可能存在最长路径显然有三种:
- 不经过根节点u即最长路径在子树里
- 路径的一个端点就是跟节点u,即子树中的一点v与u构成路径
- 经过u,路径的两端在两个子树中,路径经过u
设f[x]表示当前子树中节点v到根节点u的路径的异或和等于x的结点v的最大深度
对于第一个情况:答案为ans[u]=max(ans[v]),v是u的子节点
对于第二个情况:ans[u]=max( max(f[Xor[u]],f[Xor[u] ^ 2i])-dep[u] ) ,Xor[u] ^ 2i相当于枚举路径的另一个端点
对于第三个情况用到树上启发式合并统计贡献
先算轻儿子,回答询问后,情况轻儿子的贡献,然后处理重儿子,保留重儿子的贡献fi,然后再计算一次剩余的轻儿子,保留贡献在fi中,用于与上层合并。此时经过节点u的路径(x,y)的长度,max(f[ xor[x] ],f[y]) - depth[u] +depth[x]-depth[u], y = xor[x] ^ 2i
代码:
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int inf = 1e8;
const int N = 5e5+10;
const db EPS = 1e-9;
using namespace std;
void dbg() {cout << "\\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int n,a[N],tot,head[N],sz[N],son[N],f[1<<22],dep[N],ans[N]; //f[i]:表示子树中异或和为f[i]的最大深度
struct Node{
int to,next;
}e[N];
void add(int x,int y){
e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}
void dfs1(int x){
ans[x] = -inf; sz[x] = 1;
for(int i = head[x]; i; i = e[i].next){
int y = e[i].to;
dep[y] = dep[x]+1; a[y] ^= a[x];
dfs1(y); sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
}
void Delete(int x){
f[a[x]] = -inf;
for(int i = head[x]; i; i = e[i].next) Delete(e[i].to);
}
void modify(int x,int fa){
ans[fa] = max(ans[fa],f[a[x]]+dep[x]-2*dep[fa]);
for(int i = 0; i < 22; i++)
ans[fa] = max(ans[fa],f[a[x]^(1<<i)]+dep[x]-2*dep[fa]);
for(int i = head[x]; i; i = e[i].next) modify(e[i].to,fa);
}
void ins(int x){
f[a[x]] = max(f[a[x]],dep[x]);
for(int i = head[x]; i; i = e[i].next) ins(e[i].to);
}
void dfs2(int x){
ans[x] = 0;
for(int i = head[x]; i; i = e[i].next)
if(e[i].to != son[x]) dfs2(e[i].to), Delete(e[i].to);
if(son[x]) dfs2(son[x]);
f[a[x]] = max(f[a[x]],dep[x]);
//路径经过x
for(int i = head[x]; i; i = e[i].next)
if(e[i].to != son[x]) modify(e[i].to,x), ins(e[i].to);
//x为路径端点
ans[x] = max(ans[x],f[a[x]]-dep[x]);
for(int i = 0; i < 22; i++)
ans[x] = max(ans[x],f[a[x]^(1<<i)]-dep[x]);
//路径不经过x
for(int i = head[x]; i; i = e[i].next) ans[x] = max(ans[x],ans[e[i].to]);
}
int main()
{
scanf("%d",&n); tot = 1;
rep(i,0,(1<<22)-1) f[i] = -inf; //不要忘记赋初值
rep(i,2,n){
int p; char s[10];
scanf("%d%s",&p,s);
add(p,i); a[i] = 1<<(s[0]-'a');
}
dfs1(1); dfs2(1);
rep(i,1,n) printf("%d%c",ans[i]," \\n"[i==n]);
return 0;
}
以上是关于CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths的主要内容,如果未能解决你的问题,请参考以下文章
「CF741D」Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
(树上启发式合并)CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
CF741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths
CF741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths
codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
cfodeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths