题目描述

一棵根为\(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. Unity坑之 传递默认枚举类型参数

    今天在编写一个通用模块的时候,遇到一个奇怪的问题,vs编译时没有任何问题,但是轮到unity编译时,却报错: error CS0103: The name `PrintInt' does not ex ...

  2. 记录一道有意思的js题目

    偶然机会,在codewars上面开始做题,遇到一道有意思的题目,记录一下: 题目是这样的: In this kata, you will write a function that returns t ...

  3. Java远程连接Linux服务器并执行命令及上传文件

    最近再开发中遇到需要将文件上传到Linux服务器上,至此整理代码笔记. 此种连接方法中有考虑到并发问题,在进行创建FTP连接的时候将每一个连接对象存放至 ThreadLocal<Ftp> ...

  4. 基于arm v8搭建区块链环境

    服务器信息: cpu:华为鲲鹏 cpu架构:arm v8 系统:CenOS-AltArch 7.6 相关工具安装 yum更新 yum update 安装vim/gcc/git/curl工具软件 yum ...

  5. Java获取Linux上指定文件夹下所有第一级子文件夹

    说明:需要只获得第一级文件夹目录 package com.sunsheen.jfids.studio.monitor.utils; import java.io.BufferedReader; imp ...

  6. MyBatis开发重点知识

    1.1为什么需要ORM框架? 传统的JDBC编程存在的弊端: ü 工作量大,操作数据库至少要5步: ü 业务代码和技术代码耦合: ü 连接资源手动关闭,带来了隐患: MyBatis前身是iBatis, ...

  7. 使用SVG symbols建立图标系统

    在实现Web项目的图标系统时,SVG是一个不错的选择.虽然使用SVG创建图标系统有多种方式.在这篇文章中,我们只看其中一种:SVG symbols.这项技术基于两个元素的使用:<symbol&g ...

  8. console.info(sum(1, 2, 3, 4)(5)(6));

     function add() {    // 第一次执行时,定义一个数组专门用来存储所有的参数    var _args = [].slice.call(arguments); // 在内部声明一个 ...

  9. TP6.0 获取请求对象的五种方式

    目录 1. 门面类 2. 依赖注入 3. 框架提供的基础控制器的 request 属性 4. request() 助手函数 5. app() 超级助手函数 think\Request.think\fa ...

  10. Java面试炼金系列 (1) | 关于String类的常见面试题剖析

    Java面试炼金系列 (1) | 关于String类的常见面试题剖析 文章以及源代码已被收录到:https://github.com/mio4/Java-Gold 0x0 基础知识 1. '==' 运 ...