CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths (dsu on tree) 题解
先说一下dsu算法。
例题:子树众数问题。
给出一棵树,每个点有点权,求每个子树中出现次数最多的数的出现次数。
树的节点数为n,\(n \leq 500000\)
这个数据范围,\(O(n \sqrt n)\)直接莫队会超时。
考虑一种暴力做法:
开一个全局数组,记录每中数的出现次数。
依次对每个点,用dfs遍历它的子树,并记录每种数的出现次数。
遍历结束后,找到众数,记录结果,并清空这个数组,进行下一次遍历。
可以发现,在最坏情况(例如一条链),算法的时间复杂度是\(O(n^2)\)的。
但是,对于一条链,只要从下至上扫描一遍,就能得出解。
而这样就相当于子树统计后的数组没有清空,而是留给了父节点。
所以,考虑这种优化:
(除根以外)每个结点都有它的父节点,所以,其实无须清空数组,直接留给父节点就行。
但是,如果一个节点的所有子结点都不删除,会很费空间,而且,我们还要将这些子树信息都合并,而合并也是\(O(n)\)的,每个节点都要合并,所以总复杂度还是\(O(n^2)\)的。
所以,对于每个节点,只能保留一个子结点,其余的还要清空。
为了使效率最高,应该保存子结点数量最多的儿子(即重儿子)。
优化后的时间复杂度:
每个节点,只有在祖先结点到父亲的是轻边时,才会被计算,所以每个最多会算\(O(\log n)\)次。总时间复杂度就优化到了\(O(n \log n)\)。
要自底向上遍历每个节点。对于每个节点,先遍历轻儿子,再遍历重儿子,最后计算它自己。
如果它到父亲的是轻边,就清空数组,否则不清空。
对于本题:
给一棵树,边上有一个字母(只有a到v共22个)。一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串。
求每个子树中最长的Dokhtar-kosh路径的长度。
可以枚举每个点,然后求答案。
枚举每个点的子节点,依次将状压值放入数组求解。
注意卡常。
代码:
#include <stdio.h>
#define re register
#pragma GCC optimize("O3")
int fr[500001],ne[500001];
int v[500001],w[500001],bs=0;
int zd[4194304],zh[500001];
int jg[500001],sd[500001];
int son[500001];
int st[500001],tp=0;
bool so[500001];
void addb(int a,int b,int c)
{
v[bs]=b;
w[bs]=c;
ne[bs]=fr[a];
fr[a]=bs;
bs+=1;
}
#define max(x,y) (x)>(y)?(x):(y)
int dfs1(int u,int s)
{
sd[u]=s;
son[u]=-1;
int ma=-1,rt=1;
for(re int i=fr[u];i!=-1;i=ne[i])
{
int t=dfs1(v[i],s+1);
rt+=t;
if(t>ma)
{
ma=t;
son[u]=v[i];
}
}
if(son[u]!=-1)
so[son[u]]=true;
return rt;
}
void dfs2(int u,int z)
{
zh[u]=z;
for(re int i=fr[u];i!=-1;i=ne[i])
dfs2(v[i],z^(1<<w[i]));
}
void solve(re int u,re int wz)
{
st[tp++]=u;
if(zd[zh[u]]!=-1)
jg[wz]=max(jg[wz],zd[zh[u]]+sd[u]);
for(int i=0;i<22;i++)
{
if(zd[zh[u]^(1<<i)]!=-1)
jg[wz]=max(jg[wz],zd[zh[u]^(1<<i)]+sd[u]);
}
for(re int i=fr[u];i!=-1;i=ne[i])
solve(v[i],wz);
}
void clean(re int u)
{
zd[zh[u]]=-1;
for(re int i=fr[u];i!=-1;i=ne[i])
clean(v[i]);
}
void dfs3(re int u)
{
for(re int i=fr[u];i!=-1;i=ne[i])
{
if(v[i]!=son[u])
dfs3(v[i]);
}
if(son[u]!=-1)
dfs3(son[u]);
for(re int i=fr[u];i!=-1;i=ne[i])
{
if(v[i]==son[u])
continue;
tp=0;
solve(v[i],u);
for(re int j=0;j<tp;j++)
zd[zh[st[j]]]=max(zd[zh[st[j]]],sd[st[j]]);
}
if(zd[zh[u]]!=-1)
jg[u]=max(jg[u],zd[zh[u]]+sd[u]);
for(int i=0;i<22;i++)
{
if(zd[zh[u]^(1<<i)]!=-1)
jg[u]=max(jg[u],zd[zh[u]^(1<<i)]+sd[u]);
}
if(!so[u])
clean(u);
else
zd[zh[u]]=max(zd[zh[u]],sd[u]);
}
void dfs4(int u)
{
jg[u]-=sd[u]*2;
if(jg[u]<0)
jg[u]=0;
for(int i=fr[u];i!=-1;i=ne[i])
{
dfs4(v[i]);
if(jg[v[i]]>jg[u])
jg[u]=jg[v[i]];
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
fr[i]=-1;
for(int i=2;i<=n;i++)
{
int f;
char ch[2];
scanf("%d%s",&f,ch);
addb(f,i,ch[0]-'a');
}
for(int i=0;i<4194304;i++)
zd[i]=-1;
dfs1(1,0);
dfs2(1,0);
dfs3(1);
dfs4(1);
for(int i=1;i<=n;i++)
printf("%d ",jg[i]);
return 0;
}
CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths (dsu on tree) 题解的更多相关文章
- CF 741D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths [dsu on tree 类似点分治]
D. Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths CF741D 题意: 一棵有根树,边上有字母a~v,求每个子树中最长的边,满 ...
- [Codeforces741D]Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths——dsu on tree
题目链接: Codeforces741D 题目大意:给出一棵树,根为$1$,每条边有一个$a-v$的小写字母,求每个点子树中的一条最长的简单路径使得这条路径上的边上的字母重排后是一个回文串. 显然如果 ...
- Codeforces.741D.Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree 思路)
题目链接 \(Description\) 给定一棵树,每条边上有一个字符(a~v).对每个节点,求它的子树中一条最长的路径,满足 路径上所有边上的字符可以重新排列成一个回文串.输出其最长长度. \(n ...
- CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths——dsu on tree
题目描述 一棵根为1 的树,每条边上有一个字符(a-v共22种). 一条简单路径被称为Dokhtar-kosh当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求每个子树中最长的Dokhtar- ...
- codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
题目链接:Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 第一次写\(dsu\ on\ tree\),来记录一下 \(dsu\ o ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- [探究] dsu on tree,一类树上离线问题的做法
dsu on tree. \(\rm 0x01\) 前言\(\&\)技术分析 \(\bold{dsu~on~tree}\),中文别称"树上启发式合并"(虽然我并不承认这种称 ...
- CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 好像这个题只能Dsu On Tree? 有根树点分治 统计子树过x的 ...
- 【DSU on tree】【CF741D】Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
Description 给定一棵 \(n\) 个节点的树,每条边上有一个字符,字符集大小 \(22\),求每个节点的子树内最长的简单路径使得路径上的字符经过重排后构成回文串. Limitation \ ...
随机推荐
- java 中的容器(札记)
创建容器向上转型为接口的时候,有时候,并不是一定可行的,因为有的实现类,在接口的基础添加了自己的方法:比如:List 接口下面的 LinkedList 自己定义了一些方法 : Arrays.asLis ...
- jQuery笔记归纳
使用jQuery前首先需要导如jQuery包:https://jquery.com/(点击下载按钮,进入后点击鼠标右键另存为即可下载) 例如:<script type="te ...
- python3 字符集的应用
python3的字符集测试 s_test=u"严" print(s_test.encode('gbk')) print([s_test]) #print(s_test[]) #pr ...
- Python开发【第五章】:常用模块
一.模块介绍: 1.模块定义 用来从逻辑上组织python代码(变量,函数,类,逻辑:实现一个功能),本质上就是.py结尾python文件 分类:内置模块.开源模块.自定义模块 2.导入模块 本质:导 ...
- 1.ASP.NET Core Docker学习-Docker介绍与目录
Docker的优点: 1节约时间,快速部署和启动 2节约成本 3标准化应用发布 4方便做持续集成 5可以用Docker做为集群中的轻量主机或节点 6方便构建基于SOA或者微服务架构 的系统 学习目录: ...
- 0160 十分钟看懂时序数据库(I)-存储
摘要:2017年时序数据库忽然火了起来.开年2月Facebook开源了beringei时序数据库:到了4月基于PostgreSQL打造的时序数据库TimeScaleDB也开源了,而早在2016年7月, ...
- win10 amd显卡开机黑屏很久
转载自:https://jingyan.baidu.com/article/3c48dd34844e0ce10ae35865.html 升级win10后,使用a卡的小伙伴应该会大为恼火,开机竟然需要黑 ...
- MVC5项目转.Net Core 2.2学习与填坑记录(1)
流程都是自己摸索,错误地方随便指正... 老项目过于臃肿,并且所有请求都是提交到一个api中,这样当api挂掉的时候,基本所有的项目都瘫痪掉了. 在4月底的时候,下决心将项目用微服务进行重写,刚开始的 ...
- springboot启动流程(十一)aop切面处理过程
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 spring的两大核心就是ioc和aop.在关于ioc依赖注入的文章中,我们了解了如何根 ...
- React/Refs and this DOM
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素. 何时使用Refs 管理焦点,文本选择或媒体播放. 触发强制动画. 集成第三方 DOM 库. 避 ...