【CodeForces】600 E. Lomsat gelral (dsu on tree)

【题目】E. Lomsat gelral

【题意】给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σci(一棵子树可能有多种占领颜色)。1<=n,ci<=10^5。

【算法】dsu on tree

【题解】入门题,讲一下dsu on tree算法。

一、dsu on tree的适用范围:①子树询问,②支持数组上的快速信息加,③不带修。(注意不需要支持信息删除,只需要能清空信息)

如果写暴力的时候,每个点需要开一个O(n)的数组,然后要从下往上的合并数组,那么dsu on tree就可以帮你把复杂度降低到O(n log n)。

二、dsu on tree的过程

①递归处理轻儿子,不保存信息。

②递归处理重儿子,保存信息。

③暴力加入该子树处理重儿子子树外的所有点的信息,得到该子树的答案。

④如果父边是轻边,扫描整棵子树清空信息。(操作必须和点数相关才能保证复杂度)

三、dsu on tree的复杂度证明

算法思想是先处理轻儿子并且不保存,然后处理重儿子并且保存信息,这样每个点暴力扫描子树都不会包含重儿子子树

轻重链剖分的特点是每个点到根至多经过log n条轻边,所以一般只要支持快速处理重链。(这个证明可以考虑最左端的底层节点,每跳一次轻边其右儿子都必须翻倍)

dsu on tree中每个点只会在其到根的路径中的所有轻边被遍历到1次,所以每个点的复杂度是O(log n)的。

总复杂度O(n log n)。

 

对于本题,需要支持快速信息加,记a[i]表示颜色i的出现次数,mx表示最多出现次数,sum表示答案,加的时候更新一下,清空的时候直接mx=sum=0即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int c[maxn],first[maxn],tot,sz[maxn],w[maxn],a[maxn],n,mx;
long long ans[maxn],sum;
bool vis[maxn];
struct edge{int v,from;}e[maxn*2];
void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
void predfs(int x,int fa){
    sz[x]=1;
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        predfs(e[i].v,x);
        sz[x]+=sz[e[i].v];
        if(sz[e[i].v]>sz[w[x]])w[x]=e[i].v; 
    }
}
void add(int x,int fa,int k){
    a[c[x]]+=k;
    if(k>0){
        if(a[c[x]]>mx){
            mx=a[c[x]];sum=c[x];
        }
        else if(a[c[x]]==mx)sum+=c[x];
    }
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&!vis[e[i].v])add(e[i].v,x,k);
}
void dfs(int x,int fa,int k){
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&e[i].v!=w[x])dfs(e[i].v,x,0);
    if(w[x])dfs(w[x],x,1),vis[w[x]]=1;
    add(x,fa,1);
    if(w[x])vis[w[x]]=0;
    ans[x]=sum;
    if(!k)add(x,fa,-1),mx=sum=0;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        insert(u,v);insert(v,u);
    }
    predfs(1,0);dfs(1,0,1);
    for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
    return 0;
}
View Code

 

posted @ 2018-04-22 12:54  ONION_CYC  阅读(270)  评论(0编辑  收藏  举报