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

题:https://codeforces.com/problemset/problem/600/E

题意:一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和,对于每个结点都输出答案。

分析:考虑暴力算法,对于每个节点只是清空计数数组,再对其子树颜色进行统计,复杂度o(n^2);

   接着我们发现最后一个子树的清空是必要的,所以我们把重儿子当作这个子树,就可以让复杂度降为o(nlogn)级别;

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
const int M=2e5+5;
ll ans[M];
ll countt[M],sz[M],son[M],col[M],vis[M];
vector<int>g[M];
ll nownum,maxx;
void dfs1(int u,int fa){
    sz[u]=1;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v!=fa){
            dfs1(v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])
                son[u]=v;
        }
    }
}

void update(int u,int k,int fa){//k的取值为1和-1,分别对应累加和清除
    countt[col[u]]+=k;
    if(maxx<countt[col[u]])
        maxx=countt[col[u]],nownum=col[u];
    else if(maxx==countt[col[u]])
        nownum+=col[u];
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v!=fa&&!vis[v])
            update(v,k,u);
    }
}
void dfs2(int u,int fa,int sign){
    //cout<<u<<endl;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v!=fa&&son[u]!=v)
            dfs2(v,u,0);
    }
    if(son[u])//再访问重儿子,一定要打上vis标记
        dfs2(son[u],u,1),vis[son[u]]=1;
    update(u,1,fa);
    ans[u]=nownum;
    if(son[u])
        vis[son[u]]=0;
    if(!sign)///轻儿子就消除影响
        update(u,-1,fa),nownum=maxx=0;

}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&col[i]);
    for(int u,v,i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        g[u].pb(v);
        g[v].pb(u);
    }
    dfs1(1,0);

    dfs2(1,0,0);
    for(int i=1;i<=n;i++)
        printf("%I64d ",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/starve/p/12233144.html

时间: 2024-08-01 01:18:59

600E - Lomsat gelral(找子树多颜色问题)(入门)的相关文章

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学习)

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

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) 计算轻儿子子树的答案,并删掉轻儿子的贡献(大多数时候): 计算重儿子子树的答案,保留重儿

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【树上启发式合并】

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

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'

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 \ 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$ 具体流程如下: 每次先递归计算轻儿子,再单独递归重儿子,计算完后轻儿子的一些信息

CF 600 E. Lomsat gelral

E. Lomsat gelral http://codeforces.com/contest/600/problem/E 题意: 求每个子树内出现次数最多的颜色(如果最多的颜色出现次数相同,将颜色编号求和). 分析: dsu on tree. 这个可以解决一系列不带修改的子树查询问题. 考虑暴力的思路:就是枚举每个子树,计算每个颜色出现的个数.统计答案. dsu on tree:最后一个子树枚举计算完了,它的贡献可以保留,然后用其它的子树去合并.(最后一棵子树是最大的).现在的复杂度就是nlog