dsu on tree

题目链接

点我跳转

题目大意

一棵根为 \(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种)

一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。

求每个子树中最长的Dokhtar-kosh路径的长度。

解题思路

\(dsu\) \(on\) \(tree\) + 状态压缩

1.重排后构成回文的条件为:

​ ①.每个字母出现的次数都为偶数 ②.一个字母出现次数为奇数,其余字母出现次数为偶数

2.字母的范围为 a ~ z , 把其转换成二进制状态(偶数为0,奇数为1)

​ 那么满足回文条件的二进制为 : 00...000 , 00..001 , 00..010 , ... , 10..000

3.维护一个从根节点到子节点u的前缀异或和数组 X

​ 那么 u 到 v 的简单路径的字母重排后的二进制形式为 X[u] ^ X[v]

4.节点 u 的答案有三种可能:

​ ①.它的两个不同分支的节点构成的简单路径 ②.它的一个分支到它本身构成的简单路径 ③.它的子节点的 ans

于是就可以定义 \(f_x\) 表示状态为 \(x\) 的节点的最大深度

那么当根节点为 \(rt\) 时 , 子节点 \(u\) 和 \(rt\) 产生的贡献为 ↓

if((x[u] ^ x[rt]) == 0) ma = max(ma , dep[u] - dep[rt]);
rep(i , 0 , 21) if((x[u] ^ x[rt]) == (1LL << i)) ma = max(ma , dep[u] - dep[rt]);

子节点 \(u\) 和 \(rt\) 的其它分支的产生贡献为 ↓

if(f[x[u]]) ma = max(ma , f[x[u]] + dep[u] - 2 * dep[rt]);
rep(i , 0 , 21)
{
int now = f[x[u] ^ (1LL << i)];
if(now) ma = max(ma , dep[u] + now - 2 * dep[rt]);
}

\(rt\) 由它轻子儿子传递上来的贡献为 ↓

for(int i = head[rt] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far || v == hson[rt]) continue ;
dsu(v , rt , 0);
ans[rt] = max(ans[rt] , ans[v]);
}

\(rt\) 由它重儿子传递上来的贡献为 ↓

if(hson[rt]) dsu(hson[rt] , rt , 1) , ans[rt] = max(ans[rt] , ans[hson[rt]]) , HH = hson[rt];
if(f[x[rt]]) ma = max(ma , f[x[rt]] - dep[rt]);
rep(i , 0 , 21) if(f[x[rt] ^ (1LL << i)]) ma = max(ma , f[x[rt] ^ (1LL << i)] - dep[rt]);

因为要计算不同分支的两点产生的贡献,所以需要先对一个分支统计完贡献后,再添加它的信息

(事实上相同分支内的节点答案的在根节点为 \(rt\) 的子节点的时候就已经算过了)

AC_Code

#include<bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define int long long
using namespace std;
const int N = 6e5 + 10;
struct Edge{
int nex , to;
}edge[N << 1];
int head[N] , TOT;
void add_edge(int u , int v)
{
edge[++ TOT].nex = head[u] ;
edge[TOT].to = v;
head[u] = TOT;
}
int hson[N] , HH , sz[N] , dep[N] , x[N];
int n , ma , a[N] , ans[N] , f[N * 20];
void dfs(int u , int far , int now)
{
sz[u] = 1;
dep[u] = dep[far] + 1;
x[u] = now ^ a[u];
for(int i = head[u] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far) continue ;
dfs(v , u , x[u]);
sz[u] += sz[v];
if(sz[v] > sz[hson[u]]) hson[u] = v;
}
}
void calc(int u , int far , int rt)
{
if(f[x[u]]) ma = max(ma , f[x[u]] + dep[u] - 2 * dep[rt]);
if((x[u] ^ x[rt]) == 0) ma = max(ma , dep[u] - dep[rt]);
rep(i , 0 , 21)
{
if((x[u] ^ x[rt]) == (1LL << i)) ma = max(ma , dep[u] - dep[rt]);
int now = f[x[u] ^ (1LL << i)];
if(now) ma = max(ma , dep[u] + now - 2 * dep[rt]);
}
for(int i = head[u] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far || v == HH) continue ;
calc(v , u , rt);
}
}
void change(int u , int far , int val)
{
if(val == 1) f[x[u]] = max(dep[u] , f[x[u]]);
else f[x[u]] = 0;
for(int i = head[u] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far || v == HH) continue ;
change(v , u , val);
}
}
void dsu(int u , int far , int op)
{
for(int i = head[u] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far || v == hson[u]) continue ;
dsu(v , u , 0);
ans[u] = max(ans[u] , ans[v]);
}
if(hson[u]) dsu(hson[u] , u , 1) , HH = hson[u] , ans[u] = max(ans[u] , ans[hson[u]]);
if(f[x[u]]) ma = max(ma , f[x[u]] - dep[u]);
rep(i , 0 , 21)
{
if(f[x[u] ^ (1LL << i)]) ma = max(ma , f[x[u] ^ (1LL << i)] - dep[u]);
}
for(int i = head[u] ; i ; i = edge[i].nex)
{
int v = edge[i].to;
if(v == far || v == hson[u]) continue ;
calc(v , u , u);
change(v , u , 1);
}
ans[u] = max(ans[u] , ma);
f[x[u]] = max(f[x[u]] , dep[u]);
HH = 0;
if(!op)
{
ma = 0;
change(u , far , -1);
}
}
signed main()
{
cin >> n;
rep(i , 2 , n)
{
int x ; char op;
cin >> x >> op;
add_edge(i , x) , add_edge(x , i);
a[i] = (1LL << (int)(op - 'a'));
}
dfs(1 , 0 , 0);
dsu(1 , 0 , 0);
rep(i , 1 , n) cout << ans[i] << " \n"[i == n];
return 0;
}

Codeforces741D的更多相关文章

  1. [Codeforces741D]Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths——dsu on tree

    题目链接: Codeforces741D 题目大意:给出一棵树,根为$1$,每条边有一个$a-v$的小写字母,求每个点子树中的一条最长的简单路径使得这条路径上的边上的字母重排后是一个回文串. 显然如果 ...

  2. codeforces741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  3. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

随机推荐

  1. 全网通4G工业路由器模块和串口转网口/4G/有线/WiFi/LTE模块的实现原理

    随着现在信息化的高速发展,网络信息的需求量大增,在移动的4G流量的场合比如汽车上实现WiFi网络覆盖,户外wifi网络覆盖需求下,4G流量已经明显不够用,而网线到达的成本比较大,难以管控.在这市场痛点 ...

  2. zookeeper在Dubbo中角色与作用

    作者:倪炜链接:http://www.zhihu.com/question/25070185/answer/86166486来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处 ...

  3. java数据结构-05双向链表

    一.双向链式存储: ①简述:要是节点中包含两个指针部分,一个指向前驱元,一个指向后继元,Java中LinkedList集合类的实现就是双向链表 (以下图片为网络收集,侵删) ②特点:数据是非连续的,链 ...

  4. NB-IOT覆盖范围有多大 NB-IOT的强覆盖是怎么实现的

    NB-IoT技术自出现以来就以其强大的覆盖范围和通讯距离长而受到广泛的欢迎,发展到现在已经成为万物互联网络中的一个重要分支.那么NB-IoT覆盖范围到底有多大,是怎么来衡量其覆盖能力? 强大的覆盖范围 ...

  5. Java学习的第三十七天

    1.例3.1求一元二次方程的根 import java.util.Scanner; public class cjava { public static void main(String[]args) ...

  6. Struts2 S2-059 (CVE-2019-0230 )复现 及流量分析、特征提取

    一.简介 2020年08月13日,Apache官方发布了Struts2远程代码执行漏洞的风险通告,该漏洞编号为CVE-2019-0230,漏洞等级:高危,漏洞评分:8.5 二.漏洞描述 Struts2 ...

  7. python数据分析使用matplotlib绘图

    matplotlib绘图 关注公众号"轻松学编程"了解更多. Series和DataFrame都有一个用于生成各类图表的plot方法.默认情况下,它们所生成的是线形图 %matpl ...

  8. uniapp微信小程序获取当前用户手机号码(前端)

    按钮触发获取用户信息 uniapp中与微信小程序官网所写会不同, <button open-type="getPhoneNumber" @getphonenumber=&qu ...

  9. 【SpringCloud】06.Eureka 总结

    1.两个注解: @EnableEurekaServer--在启动类上添加 @EnableDiscoveryClient或@EnableEurekaClient--启动类加 因为Eureka支持多种注册 ...

  10. MYSQL的添加字段和修改字段

    ALTER TABLE 表名 ADD 字段名 字段类型 //添加字段 DESC 表名 //获取表的结构 SHOW COLUMNS FROM 表名 //也是获取表的结构 DESCRIBE 表名 //获取 ...