Codeforces.600E.Lomsat gelral(dsu on tree)

题目链接

dsu on tree参见这

\(Description\)

给定一棵树。求以每个点为根的子树中,出现次数最多的颜色的和。

\(Solution\)

dsu on tree模板题.
用sum[i]表示出现次数为i的颜色的和,cnt[i]表示出现次数为i的颜色有多少个(其实有个Max表示当前最多的次数,和tm[i]就好了),然后就这样了。。



再写一遍dsu on tree大体过程:(设当前点为x)
计算轻儿子子树的答案,并删掉轻儿子的贡献(大多数时候);
计算重儿子子树的答案,保留重儿子的贡献(有时候不需要?);
加入x轻儿子子树的贡献;
得到x的答案;
如果x不是重儿子,则删掉它整个子树的贡献(本题直接将Max,Sum清0就好了)。

复杂度证明:每个点会在它上面的每条轻边处暴力统计一次。而每个点到根节点的路径上只有\(O(\log n)\)条轻边。



可以用DFS序代替DFS减少一些常数。

优化:(DFS序)

//62ms  13400KB
#include <cstdio>
#include <cctype>
#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=1e5+5;

int skip,Enum,H[N],nxt[N<<1],to[N<<1],Max,col[N],sz[N],son[N],tm[N],A[N],L[N],R[N];
LL Sum,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)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x,int fa)
{
    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])
        if((v=to[i])!=fa)
        {
            DFS1(v,x), sz[x]+=sz[v];
            if(sz[v]>mx) mx=sz[v], son[x]=v;
        }
    R[x]=Index;
}
inline void Add(int c)
{
    if(++tm[c]>Max) Max=tm[c], Sum=c;
    else if(tm[c]==Max) Sum+=c;
}
void Solve(int x,int fa,int keep)
{
    for(int i=H[x]; i; i=nxt[i])
        if(to[i]!=fa && to[i]!=son[x]) Solve(to[i],x,0);
    if(son[x]) Solve(son[x],x,1);

    Add(col[x]);
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=fa && v!=son[x])
            for(int j=L[v]; j<=R[v]; ++j) Add(col[A[j]]);
    Ans[x]=Sum;
    if(!keep)
    {
        Max=Sum=0;
        for(int i=L[x]; i<=R[x]; ++i) --tm[col[A[i]]];
    }
}

int main()
{
    int n=read();
    for(int i=1; i<=n; ++i) col[i]=read();
    for(int i=1; i<n; ++i) AE(read(),read());
    DFS1(1,1), Solve(1,1,1);
    for(int i=1; i<=n; ++i) printf("%I64d ",Ans[i]);

    return 0;
}

未优化:

//93ms  9100KB
#include <cstdio>
#include <cctype>
#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=1e5+5;

int skip,Enum,H[N],nxt[N<<1],to[N<<1],Max,col[N],sz[N],son[N],tm[N];
LL Sum,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)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x,int fa)
{
    int mx=0; sz[x]=1;
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=fa)
        {
            DFS1(v,x), sz[x]+=sz[v];
            if(sz[v]>mx) mx=sz[v], son[x]=v;
        }
}
void Add(int x,int fa)
{
    if(++tm[col[x]]>Max) Max=tm[col[x]], Sum=col[x];
    else if(tm[col[x]]==Max) Sum+=col[x];
    for(int i=H[x]; i; i=nxt[i])
        if(to[i]!=fa && to[i]!=skip) Add(to[i],x);
}
void Del(int x,int fa)
{
    --tm[col[x]];
    for(int i=H[x]; i; i=nxt[i])
        if(to[i]!=fa) Del(to[i],x);
}
void Solve(int x,int fa,int keep)
{
    for(int i=H[x]; i; i=nxt[i])
        if(to[i]!=fa && to[i]!=son[x]) Solve(to[i],x,0);
    if(son[x]) Solve(son[x],x,1), skip=son[x];
    Add(x,fa), Ans[x]=Sum, skip=0;
    if(!keep) Max=Sum=0, Del(x,fa);
}

int main()
{
    int n=read();
    for(int i=1; i<=n; ++i) col[i]=read();
    for(int i=1; i<n; ++i) AE(read(),read());
    DFS1(1,1), Solve(1,1,1);
    for(int i=1; i<=n; ++i) printf("%I64d ",Ans[i]);

    return 0;
}

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

时间: 2024-10-08 08:53:52

Codeforces.600E.Lomsat gelral(dsu on tree)的相关文章

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

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

CF 600E. Lomsat gelral(dsu on tree)

解题思路 \(dsu\) \(on\) \(tree\)的模板题.暴力而优雅的算法,轻儿子的信息暴力清空,重儿子的信息保留,时间复杂度\(O(nlogn)\) 代码 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<set> using namespace std; const int

Codeforces 600E Lomsat gelral (Dsu On the Tree)

题目链接 Lomsat gelral 占坑--等深入理解了再来补题解-- 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 typedef long long LL; 8 9 const int N = 600010; 10 11 int n; 12 int cc[N], col[N], sz[N], son[N];

Codeforces 600E - Lomsat gelral 「$Dsu \ on \ tree$模板」

With $Dsu \ on \ tree$ we can answer queries of this type: How many vertices in the subtree of vertex $v$ has some property in $O (n \log n)$ time (for all of the queries)? 这题写的是轻重儿子(重链剖分)版本的 $Dsu \ on \ tree$ 具体流程如下: 每次先递归计算轻儿子,再单独递归重儿子,计算完后轻儿子的一些信息

CodeForces 600E. Lomsat gelral【树上启发式合并】

传送门 好像大家都是拿这道题作为树上启发式合并的板子题. 树上启发式合并,英文是 dsu on tree,感觉还是中文的说法更准确,因为这个算法和并查集(dsu)没有任何关系.一般用来求解有根树的所有子树的统计问题. 根据轻重儿子的各种性质,可以证明这个算法的时间复杂度为 \(O(nlogn)\),虽然看起来暴力的不行,但是却是一个很高效的算法. 算法的核心其实就是对于每个节点,先计算轻儿子,再计算重儿子,把自己和轻儿子的所有贡献累计到重儿子上去,如果自己是轻儿子,就把贡献清空. 如果掌握了树链

CodeForces 600E Lomsat gelral(线段树合并)

题目链接:http://codeforces.com/problemset/problem/600/E You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour. Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the

codeforces 600E . Lomsat gelral (线段树合并)

You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour. Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c. So it'

600E - Lomsat gelral(找子树多颜色问题)(入门)

题:https://codeforces.com/problemset/problem/600/E 题意:一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和,对于每个结点都输出答案. 分析:考虑暴力算法,对于每个节点只是清空计数数组,再对其子树颜色进行统计,复杂度o(n^2); 接着我们发现最后一个子树的清空是必要的,所以我们把重儿子当作这个子树,就可以让复杂度降为o(nlogn)级别: #include<bits/stdc++.h> using n

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)