题目描述

一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种)。 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串。 求每个子树中最长的\(Dokhtar-kosh\)路径的长度。

输入输出样例

输入 #1

4

1 s

2 a

3 s

输出 #1

3 1 1 0

输入 #2

5

1 a

2 h

1 a

4 h

输出 #2

4 1 0 1 0

分析

一道树上启发式合并的好题

首先,我们来考虑什么样的情况下路径上的字符重新排列之后能够形成回文串

很显然,只有当路径上每种字母的数量都为偶数个或者有且仅有一种字母的数量是奇数个时才满足条件

这两种情况分别对应奇回文串和偶回文串

然后我们会发现字母只有 \(22\) 种,因此字母的状态可以状压

而题目的要求仅仅是判断奇偶性,因此我们用 \(0\) 表示偶数,用 \(1\) 表示奇数

那么满足要求的状态只有 \(0\) 和 \(2^i\)

那么我们就可以存储每一种状态所对应的节点的最大深度

转移时,当前的结点的 \(dp\) 值会由三种情况转移过来

1、在儿子节点的 \(dp\) 值中取 \(max\)

2、从儿子节点中选择一条链和当前的节点组成一条新的链

3、从两个不同的儿子节点中选择两条链和当前的节点组成一条新的链

为了避免出现自己更新自己的情况,我们要计算完一个儿子节点后再计算另一个儿子节点

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rg register
const int maxn=4e6+5;
int h[maxn],tot=1,n;
struct asd{
int to,nxt,val;
}b[maxn];
void ad(int aa,int bb,int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].val=cc;
h[aa]=tot++;
}
int siz[maxn],son[maxn],dep[maxn];
int f[maxn],dp[maxn],orz,ans[maxn<<2],yh[maxn],mmax,haha;
void dfs1(int now,int fa){
siz[now]=1;
dep[now]=dep[fa]+1;
f[now]=fa;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
yh[u]=yh[now]^(1<<b[i].val);
dfs1(u,now);
siz[now]+=siz[u];
if(son[now]==0 || siz[u]>siz[son[now]]){
son[now]=u;
}
}
}
void js(int now){
if(ans[yh[now]]) mmax=std::max(mmax,ans[yh[now]]+dep[now]-haha);
for(int i=0;i<=22;i++){
if(ans[yh[now]^(1<<i)])mmax=std::max(mmax,ans[yh[now]^(1<<i)]+dep[now]-haha);
//只有当ans值存在的时候才能转移
}
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==f[now] || u==orz) continue;
js(u);
}
}
//计算子树对父亲节点的贡献
void add(int now,int op){
if(op==1){
ans[yh[now]]=std::max(ans[yh[now]],dep[now]);
} else {
ans[yh[now]]=0;
}
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==f[now] || u==orz) continue;
add(u,op);
}
}
//加入或删除子树贡献
void dfs2(int now,int fa,int op){
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==f[now] || u==son[now]) continue;
dfs2(u,now,0);
dp[now]=std::max(dp[now],dp[u]);
}
if(son[now]){
dfs2(son[now],now,1);
orz=son[now];
dp[now]=std::max(dp[now],dp[son[now]]);
}
//先递归轻儿子,再递归重儿子
haha=dep[now]*2;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==orz || u==fa) continue;
js(u);
add(u,1);
}
//计算完一个子树的贡献再加入另一个子树的贡献
mmax=std::max(mmax,ans[yh[now]]-dep[now]);
for(rg int i=0;i<=22;i++){
mmax=std::max(mmax,ans[yh[now]^(1<<i)]-dep[now]);
}
//即使ans值不存在,也不会更新
ans[yh[now]]=std::max(ans[yh[now]],dep[now]);
//从子树中选择一条链和当前节点连起来
orz=0;
dp[now]=std::max(mmax,dp[now]);
if(op==0){
add(now,-1);
//清除轻儿子贡献
mmax=haha=0;
}
}
int main(){
memset(h,-1,sizeof(h));
scanf("%d",&n);
rg int aa;
char bb;
for(rg int i=2;i<=n;i++){
scanf("%d %c",&aa,&bb);
ad(aa,i,bb-'a');
ad(i,aa,bb-'a');
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++){
printf("%d ",dp[i]);
}
printf("\n");
return 0;
}

CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)的更多相关文章

  1. 神奇的树上启发式合并 (dsu on tree)

    参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...

  2. 树上启发式合并 (dsu on tree)

    这个故事告诉我们,在做一个辣鸡出题人的比赛之前,最好先看看他发明了什么新姿势= =居然直接出了道裸题 参考链接: http://codeforces.com/blog/entry/44351(原文) ...

  3. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  4. dsu on tree (树上启发式合并) 详解

    一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...

  5. dsu on tree 树上启发式合并 学习笔记

    近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...

  6. CF600E Lomsat gelral——线段树合并/dsu on tree

    题目描述 一棵树有$n$个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. 这个题意是真的窒息...具体意思是说,每个节点有一个颜色,你要找的是每个子树中颜色的众数 ...

  7. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  8. codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(启发式合并)

    codeforces 741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...

  9. dsu on tree[树上启发式合并学习笔记]

    dsu on tree 本质上是一个 启发式合并 复杂度 \(O(n\log n)\) 不支持修改 只能支持子树统计 不能支持链上统计- 先跑一遍树剖的dfs1 搞出来轻重儿子- 求每个节点的子树上有 ...

随机推荐

  1. 【JAVA】给大家推荐一道有意思的java测试题。你知道答案吗?

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985, QQ986945193 公众号:程序员小冰 public class Test { /** * * ...

  2. playable

    探索TimelinePlayableAPI,让Timeline为所欲为 https://blog.csdn.net/qq826364410/article/details/80534892 Playa ...

  3. Spring Cloud:Consul基础知识

    一.基本概念 Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go开发. 它提供微服务系统中的服务治理.配置中心.控制总线等功能. 服务发现:提供HTTP和DNS两种发 ...

  4. 08_Python的数据类型

    1.数字(整型int 浮点型float 复数complex) 1.数字概述 不可变类型,一旦创建不可修改,不是迭代对象,属于原子型 2.整型数int 概述: 整型数是不带有小数部分的数字,包括自然数, ...

  5. Agumaster点个按钮后台刷新前台一步步出状态

    --2020年5月4日--

  6. 用Java爬虫爬取凤凰财经提供的沪深A股所有股票代号名称

    要爬取的凤凰财经网址:http://app.finance.ifeng.com/list/stock.php?t=hs 本作主要采用的技术是jsoup,相关介绍网页:https://www.jians ...

  7. 转载:人家编写的程序:「雀神 AI」Suphx

    下文转载自:https://news.51cto.com/art/202004/613746.htm 按:虽然还是玩棋牌乐的AI,但相对于我们日常编写的XX管理系统,好比哺乳动物 VS 鱼类了. 20 ...

  8. 如何PJ IDEA

    1.打开:http://idea.medeming.com/jets/,点击下载PJ码 2.下载完成后解压,会得到2个文件 3.启动IDEA时,将PJ码复制到: 点击ok,就可进入idea中.接着PJ ...

  9. Python爬虫学习第一记 (翻译小助手)

    1 # Python爬虫学习第一记 8.24 (代码有点小,请放大看吧) 2 3 #实现有道翻译,模块一: $fanyi.py 4 5 import urllib.request 6 import u ...

  10. Badboy脚本录制工具

    Badboy 目录 Badboy 1.Badboy安装 2.脚本的录制 1.Badboy安装 下载地址: http://www.badboy.com.au/download/index 安装其实傻瓜式 ...