题目描述

一棵根为\(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. 剑指 Offer 56 - I. 数组中数字出现的次数

    题目描述 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是\(O(n)\),空间复杂度是\(O(1)\). 示例1: 输入:nums ...

  2. ElasticSearch7.6.1 安装及其head插件安装

    本来打算写一篇ES和Solr的差别的,后来想想算了简单说说吧, 如果是对已存在数据建立完成索引的情况下,Solr更快 一但进行索引的操作的时候,Solr的IO是阻塞的 对于大数据量的实时检索,还是El ...

  3. PHP 安装 扩展时 抛出 /usr/local/Cellar/php@7.1/7.1.25/pecl 异常解决

    liugx@MacBook-Pro  ~/work/php/ext_source/php-xhprof-extension   master  make installmkdir: /usr/ ...

  4. 为什么选择H5游戏开发定制?

    为什么选择H5游戏开发定制? 随着微信H5游戏推广带来的显著效果,越来越多的商家已经加入到游戏营销的队伍中来, 对H5小游戏有了解的商家都知道,[模板游戏]的价格往往低于[定制游戏]的价格,可是为什么 ...

  5. UNIX编程艺术

    本文主要是 <UNIX编程艺术>的摘录,摘录的主要是我觉得对从事软件开发有用的一些原则. 对于程序员和开发人员来说,如果完成某项任务所需要付出的努力对他们是个挑战却又恰好还在力所能及的范围 ...

  6. BFC与HasLayout的理解

    1.(Block Formatting Contexts)BFC 定义 BFC(Block formatting context)直译为"块级格式化上下文".它是一个独立的渲染区域 ...

  7. Docker多主机管理(八)

    docker多主机管理 前面我们的实验环境中只有一个 docker host,所有的容器都是运行在这一个 host 上的.但在真正的环境中会有多个 host,容器在这些 host 中启动.运行.停止和 ...

  8. golang 条件语句 for range 分析

    for range 作为 golang中的语法糖提供了便利操作; 对于for range 支持 的数据类型包含: 数组以及指向数组的指针 切片 字典 通道 字符串 在range的语法糖中提供了一下特殊 ...

  9. Qt 展示pdf内容(新窗口或嵌入,pdfjs,linux)

    前言:初学Qt,在网上查找了诸多资料,有什么poppler.mupdf啊巴拉巴拉的,结果一个比一个费劲,最后还是采用pdfjs较为方便高效,为方便相关问题搜索,写了一下内容. 需求描述:Qt应用中不支 ...

  10. jfinal3连接sqlserver2012 使用generator生成model 将smallint默认为string

    修改MetaBuilder的buildColumnMetas方法增加对smallint的判断