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

题目链接

\(Description\)

给定一棵树,每条边上有一个字符(a~v)。对每个节点,求它的子树中一条最长的路径,满足 路径上所有边上的字符可以重新排列成一个回文串。输出其最长长度。
\(n\leq 5\times10^5\)。

\(Solution\)

可以构成回文串,即要么所有字符都出现了偶数次,要么有一个出现了奇数次、其余都出现了偶数次。
转化为异或!把每个字符c(0~21)映射到1<<c上去。
令\(s[x]\)表示根节点到\(x\)路径上边权的异或和。那么路径\((u,v)\)满足条件当且仅当\(s[u]\ xor\ s[v]\)等于\(0\)或是某个二次幂。
而路径\((u,v)\)的答案是\(dep[u]+dep[v]-dep[LCA]*2\)。在LCA处计算,这样只需要对每个状态求它最大的\(dep\)。
而且更新时只有23种方式(对于\(s[v]\),可以从\(\max\{dep[s[v]]\}\)和\(\max\{dep[s[v] xor\ 2^i]\}\)更新)。
dsu on tree求每个子树的\(\max\{dep[s]\}\)就好了。
复杂度\(O(23n\log n)\)。

//608ms 79100KB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5,INF=0x3f3f3f3f;

int Enum,H[N],nxt[N],to[N],ch[N],s[N],f[(1<<22)+2],L[N],R[N],A[N],dep[N],sz[N],son[N],Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
inline void AE(int u,int v,int c)
{
    to[++Enum]=v, nxt[Enum]=H[u], ch[Enum]=c, H[u]=Enum;
}
void DFS1(int x)
{
    static int Index=0;
    A[L[x]=++Index]=x;
    int mx=0; sz[x]=1;
    for(int i=H[x],v; i; i=nxt[i])
        dep[v=to[i]]=dep[x]+1, s[v]=s[x]^ch[i], DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
    R[x]=Index;
}
inline int Add(int s,int d,int delta)
{
    int ans=f[s]+d-delta;
    for(int i=0; i<22; ++i) ans=std::max(ans,f[s^(1<<i)]+d-delta);//d[u]+d[v]-d[LCA]*2
    return ans;
}
void DFS2(int x,int keep)
{
    int ans=0;
    for(int i=H[x]; i; i=nxt[i]) if(to[i]!=son[x]) DFS2(to[i],0),ans=std::max(ans,Ans[to[i]]);
    if(son[x]) DFS2(son[x],1),ans=std::max(ans,Ans[son[x]]);

    ans=std::max(ans,Add(s[x],0,dep[x])), f[s[x]]=std::max(f[s[x]],dep[x]);
    for(int i=H[x],v,delta=dep[x]<<1; i; i=nxt[i])
        if((v=to[i])!=son[x])
        {
            for(int j=L[v]; j<=R[v]; ++j) ans=std::max(ans,Add(s[A[j]],dep[A[j]],delta));
            for(int j=L[v]; j<=R[v]; ++j) f[s[A[j]]]=std::max(f[s[A[j]]],dep[A[j]]);
        }
    Ans[x]=ans;
    if(!keep) for(int i=L[x]; i<=R[x]; ++i) f[s[A[i]]]=-INF;
}

int main()
{
    int n=read();
    for(int i=2,x,c; i<=n; ++i)
    {
        x=read(),c=gc(); while(!isalpha(c)) c=gc();
        AE(x,i,1<<c-'a');
    }
    memset(f,-0x3f,sizeof f);//没有的值不能用0更新
    DFS1(1), DFS2(1,1);
    for(int i=1; i<=n; ++i) printf("%d ",Ans[i]);

    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/10017233.html

时间: 2024-10-08 01:18:41

Codeforces.741D.Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree 思路)的相关文章

Codeforces 741D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

Description 以\(1\) 为根 的 \(n\) 个节点的树,每条边有一个颜色 \(x\),求每一个点的子树内的好的路径的最长长度 一条路径被定义为好的当且仅当把所有经过的边的字母经过排列之后可以变成回文 题面 Solution 理解了一下 \(dsu\,on\,tree\),相比普通的启发式,省去了高级的数据结构,并省下了大量空间 好的路径实际上就是出现奇数次的字母不多于一个,字符集只有 \(22\),可以状压起来 对于一条路径的异或和实际上可以看成 \(dis[x]\)^\(dis

cfodeforces 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\ on\ tree\)主要维护子树信息,往往可以省掉一个数据结构的启发式合并.大体思路如下: 轻重链路径剖分之后,对每个点先递归处理他的所有轻儿子,每次处理完轻儿子之后把这棵子树的信息清空.最后再来处理重孩子,重儿子的信息就可以不用清空了.由于我们是用一个全局数组来记录信息的,重儿子子树的信息就仍然保留

[探究] dsu on tree,一类树上离线问题的做法

dsu on tree. \(\rm 0x01\) 前言\(\&\)技术分析 \(\bold{dsu~on~tree}\),中文别称"树上启发式合并"(虽然我并不承认这种称谓),大概是一种优雅的暴力,并且跟\(dsu\)毫无关系.于是我打算叫他\(\bold{Elegantly~Direct~Counting~on~Tree}\),"优雅的树上暴力统计". 严格来说,\(\bold {EDCT}\)解决的问题范围并不广泛: 1.维护子树信息: 2.不能带修改

Codeforces 741 D - Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

D - Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 思路: 树上启发式合并 从根节点出发到每个位置的每个字符的奇偶性记为每个位置的状态,每次统计一下每个状态的最大深度 为了保证链经过当前节点u,我们先计算每个子树的答案,再更新子树状态对深度的贡献. 代码: #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include<bit

Codeforces 600E. Lomsat gelral(Dsu on tree学习)

题目链接:http://codeforces.com/problemset/problem/600/E n个点的有根树,以1为根,每个点有一种颜色.我们称一种颜色占领了一个子树当且仅当没有其他颜色在这个子树中出现得比它多.求占领每个子树的所有颜色之和. 我们都知道可以$BST$启发式合并从而完美${O(nlogn^{2})}$,这太丑陋了. 那么$Dsu~~on~~tree$是在干啥呢? 找出树中每一个节点的重儿子,统计答案的时候优先进入每一个点的所有轻儿子,之后再进入重儿子,目的是保留重儿子所

Codeforces 741B Arpa&#39;s weak amphitheater and Mehrdad&#39;s valuable Hoses

[题目链接] http://codeforces.com/problemset/problem/741/B [题目大意] 给出一张图,所有连通块构成分组,每个点有价值和代价, 要么选择整个连通块,要么只能在连通块中选择一个,或者不选,为最大价值 [题解] 首先我们用并查集求出连通块,然后对连通块进行分组背包即可. [代码] #include <cstdio> #include <vector> #include <algorithm> #include <cstr

codeforces 742D Arpa&#39;s weak amphitheater and Mehrdad&#39;s valuable Hoses ——(01背包变形)

题意:给你若干个集合,每个集合内的物品要么选任意一个,要么所有都选,求最后在背包能容纳的范围下最大的价值. 分析:对于每个并查集,从上到下滚动维护即可,其实就是一个01背包= =. 代码如下: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <vector> 5 using namespace std; 6 const int N = 1000 + 5;

codeforces 851D Arpa and a list of numbers

目录 codeforces 851D Arpa and a list of numbers 题意 题解 Code codeforces 851D Arpa and a list of numbers 题目传送门 题意 给出\(n\)个数,有两种操作: 1.将一个数从数列中删除,代价为\(x\). 2.将一个数加1,代价为\(y\). 询问最少花费多少的代价能够使数列中所有数的\(Gcd\)不为1. \((1 \leq n \leq 5 \cdot 10^5 , 1 \leq x,y \leq 1

Codeforces Round #615 (Div. 3) F. Three Paths on a Tree

F. Three Paths on a Tree 原题链接:https://codeforces.com/contest/1294/problem/F 题目大意: 给定一棵树,选出三点,使三点连成的j简单路径最大.简而言之,三个点连成的边的集合大小. 解题思路: 假设任取一点为三点连线的公共点,最长路径就是这个点到其他三个点的三条最长边之和,可知这个点一定在直径上(画图分析假设不在时的最长路径可反证).所以先求出树的直径,在使用$ans =(a b+a c+b c) / 2$遍历可以得到第三个点