题目描述

一棵根为\(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. 表格取消全选框,用文字表示--Echarts ElementUi

    1.先看看实现的图 一. 添加添加复选框列 <el-table v-loading="zongShipLoading" tooltip-effect="dark&q ...

  2. MonoBehaviour生命周期与对象数据池应用

    预热游戏对象: tempObject = Instantiate(cubePrefab) as GameObject ; tempObject .SetActive( false ); 游戏对象tem ...

  3. Selenium文件上传问题

     

  4. Avtiviti工作流规范 BPM与BPMN

    进过长时间的轮转,重拾Activiti,因为最近在智联上看到多家公司的需求上写的,都要熟悉工作流引擎,也就是activiti所以重拾 之前看的视屏是activiti5,我觉得版本有点低,所以打算看一下 ...

  5. [bash] 获取linux主机名,检视内中是否有特定字符串

    代码: #!/bin/bash hostname=$(hostname) #调用hostname命令获取主机名放入变量hostname中 #echo $hostname if [ `echo ${ho ...

  6. [03] C# Alloc Free编程

    C# Alloc Free编程 首先Alloc Free这个词是我自创的, 来源于Lock Free. Lock Free是说通过原子操作来避免锁的使用, 从而来提高并行程序的性能; 与Lock Fr ...

  7. 微信小程序-组件-视图容器

    1.view 1.作用:类似 html 的 div 用来进行页面布局,具有块级盒子特性. 2.常用属性:设置view盒子点击后的状态,以及控制是否影响父盒子的点击状态 3.eg:<view ho ...

  8. ASP.NET Core 性能优化最佳实践

    本文提供了 ASP.NET Core 的性能最佳实践指南. 译文原文地址:https://docs.microsoft.com/en-us/aspnet/core/performance/perfor ...

  9. [LeetCode]404. 左叶子之和(递归)、938. 二叉搜索树的范围和(递归)(BST)

    题目 404. 左叶子之和 如题 题解 类似树的遍历的递归 注意一定要是叶子结点 代码 class Solution { public int sumOfLeftLeaves(TreeNode roo ...

  10. 《Offer一箩筐》2W字总结面试套路14问——不给例子的教程都是耍流氓!!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! Hi~ 这里是 ...