【题意】给定n个点的树,每条边有一个小写字母a~v,求每棵子树内的最长回文路径,回文路径定义为路径上所有字母存在一种排列为回文串。n<=5*10^5。

【算法】dsu on tree

【题解】这题经典套路就是按照22个字母个数的奇偶性压位,然后两段路径异或起来是0或1<<j就是合法路径。

dsu的时候每个点统计其子树内经过这个点的路径,注意包括从子树到该点终止的和该点自身也要算。

那么类似点分治的方式,算完重儿子后处理一下根,然后就一棵一棵轻儿子子树和之前的子树状态桶数组统计然后加入。

传递上去的时候需要特别注意,dsu是无法支持数组的整体位移的,解决方法一般是把统计从x到子树改为从根到子树,这样所有点都是一样的,不需要位移。

当然这就需要满足信息的可减性,而深度deep和异或xor都是满足的。(xor和deep的两点间路径转两点到根路径非常经典了)

复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
bool isdigit(char c){return c>=''&&c<='';}
int read(){
int s=,t=;char c;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
using namespace std;
const int maxn=,inf=0x3f3f3f3f;
int n,sz[maxn],first[maxn],a[maxn],b[],c[],ans[maxn],w[maxn],fa[maxn],tot,deep[maxn];
struct edge{int v,from;}e[maxn*];
void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
void p(int &x,int y){if(x<y)x=y;}
void dfs_pre(int x){
sz[x]=;
for(int i=first[x];i;i=e[i].from){
deep[e[i].v]=deep[x]+;
a[e[i].v]^=a[x];
dfs_pre(e[i].v);
sz[x]+=sz[e[i].v];
if(sz[e[i].v]>sz[w[x]])w[x]=e[i].v;
}
}
void calc(int x,int tp){
for(int j=;j<=;j++)p(ans[tp],deep[x]+c[a[x]^b[j]]);
for(int i=first[x];i;i=e[i].from)calc(e[i].v,tp);
}
void add(int x,int k){
if(k)p(c[a[x]],deep[x]);
else c[a[x]]=-inf;
for(int i=first[x];i;i=e[i].from)add(e[i].v,k);
}
void dfs(int x){
for(int i=first[x];i;i=e[i].from)if(e[i].v!=w[x])dfs(e[i].v);
if(w[x])dfs(w[x]);//
p(c[a[x]],deep[x]);for(int j=;j<=;j++)p(ans[x],deep[x]+c[a[x]^b[j]]);
for(int i=first[x];i;i=e[i].from)if(e[i].v!=w[x])calc(e[i].v,x),add(e[i].v,);
if(x!=w[fa[x]])add(x,);
}
char s[];
int main(){
n=read();
for(int i=;i<=n;i++){
fa[i]=read();if(fa[i])insert(fa[i],i);
scanf("%s",s);a[i]=<<(s[]-'a');
}
b[]=;for(int j=;j<=;j++)b[j]=<<j;
for(int i=;i<(<<);i++)c[i]=-inf;
dfs_pre();dfs();
for(int i=;i<=n;i++)ans[i]-=*deep[i];
for(int i=n;i>=;i--)p(ans[fa[i]],ans[i]);
for(int i=;i<=n;i++)printf("%d ",ans[i]);
return ;
}

即使信息满足可减性,dsu on tree也不能像点分治一样删除某棵子树信息,进去统计后再加回来。因为dsu on tree必须满足【不能遍历重儿子】,否则复杂度就会爆炸。

不过如果题目要求的是除了某棵子树外的信息,就可以做,先统计所有轻儿子做除了重儿子的,然后进重儿子后统计所有轻儿子,一个一个删除来做除了某个轻儿子的。

【CodeForces】741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)的更多相关文章

  1. 【cf741】D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

    传送门 题意: 给出一颗以\(1\)为根的有根树,树边带有一个字符(\(a\)~\(v\))的信息. 输出对于每个结点,其子树内最长的简单路径并且满足边上的字符能够组成回文串. 思路: 显然最终的答案 ...

  2. 【CF741D】Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

    题意:我们称一个字符串为周驿东串当且仅当重排它的字符可以组成一个回文串. 给出一个n个点的有根树,根为1,每条边上有一个从a到v的字符,求每个点的子树中所有简单路径可以组成的周驿东串中的最长长度. n ...

  3. Codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

    感觉dsu on tree一定程度上还是与点分类似的.考虑求出跨过每个点的最长满足要求的路径,再对子树内取max即可. 重排后可以变成回文串相当于出现奇数次的字母不超过1个.考虑dsu on tree ...

  4. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

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

  5. 【Codeforces】Round #491 (Div. 2) 总结

    [Codeforces]Round #491 (Div. 2) 总结 这次尴尬了,D题fst,E没有做出来.... 不过还好,rating只掉了30,总体来说比较不稳,下次加油 A:If at fir ...

  6. 【Codeforces】Round #488 (Div. 2) 总结

    [Codeforces]Round #488 (Div. 2) 总结 比较僵硬的一场,还是手速不够,但是作为正式成为竞赛生的第一场比赛还是比较圆满的,起码没有FST,A掉ABCD,总排82,怒涨rat ...

  7. 【CodeForces】901 B. GCD of Polynomials

    [题目]B. GCD of Polynomials [题意]给定n,要求两个最高次项不超过n的多项式(第一个>第二个),使得到它们GCD的辗转次数为n.n<=150. [算法]构造 [题解 ...

  8. 【CF600E】Lomsat gelral(dsu on tree)

    [CF600E]Lomsat gelral(dsu on tree) 题面 洛谷 CF题面自己去找找吧. 题解 \(dsu\ on\ tree\)板子题 其实就是做子树询问的一个较快的方法. 对于子树 ...

  9. 【Silverlight】Bing Maps开发应用与技巧一:地图打点与坐标控件(CoordControl)

    [Silverlight]Bing Maps开发应用与技巧一:地图打点与坐标控件(CoordControl) 使用Bing Maps Silverlight Control开发中,很多时候都需要实现在 ...

随机推荐

  1. Sprint1回顾

    Sprint目标 此产品为适用于小学生使用的四则运算训练软件.关于第一期Sprint冲刺的目标,我们打算实现产品的以下几点的功能: •1.初始界面设计 •2.四则基本运算算法 •3.能产生随机式子 • ...

  2. Few-Shot/One-Shot Learning

    Few-Shot/One-Shot Learning指的是小样本学习,目的是克服机器学习中训练模型需要海量数据的问题,期望通过少量数据即可获得足够的知识. Matching Networks for ...

  3. d指针在Qt上的应用及实现(有图,很清楚)

    Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念.那么为什么d指针能实现二进制兼容呢?为了回答这个问题,首先弄清楚什么是二进制兼容?所谓二进制兼容动态库,指的是一个在老版本库下运行的程序 ...

  4. Python高级特性:Python迭代、生成器、列表生成式

    迭代 给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历称为迭代(Iteration). 在java和C语言中,迭代是通过循环list的下标来完成的,Pyth ...

  5. mysql实现消息队列

    mysql之消息队列   消息队列:在消息的传输过程中保存消息的容器. 消息队列管理器在将消息从它的源中继到它的目标时充当中间人.队列的主要目的是提供路由并保证消息的传递:如果发送消息时接收者不可用, ...

  6. 半夜思考之查漏补缺 , Spring 中的 Bean 继承机制

    这里的继承 , 不是 Java 中的继承 , 下面就总结下 Bean继承与Java继承的区别: Spring 中的子类 Bean 和父 Bean 可以是不同类型 , 但是 Java 中的继承则可保证子 ...

  7. delphi如何检索adoquery里面某一列存在的重复行?

    var IsHave:Boolean; begin adoquery.first; while(not adoquery.eof) do begin if(adoquery.fieldbyname(' ...

  8. Graph And Its Complement CodeForces - 990D(思维构造)

    题意: 图中有n个点,开始有a个连通块,然后连着的边断开,不连的边连上,变为b个连通块,输出原图的邻接矩阵. 解析: 原图中连通块大于1的图,经过上述操作后,一定变成只有1个连通块的图. 若n != ...

  9. 【刷题】BZOJ 4530 [Bjoi2014]大融合

    Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它 ...

  10. Java多线程相关的

    很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...