Codeforces 600E :Lomsat gelral(启发式合并)

来源:互联网 发布:时间轴画图软件 编辑:程序博客网 时间:2024/06/05 10:52

传送门

题意:
给一棵树,每个点有一个颜色,定义一个点的权值为其子树中最多的颜色(若有多个则是他们的和),求每个点的权值。

题解:

好题!!!(虽然题解是很一般的启发式合并)

首先可以Splay启发式合并一波,可以得到理论上的O(nlogn)复杂度,不过有一种跑得飞快的启发式合并方法,基本接近O(n)(实际复杂度是O(nlogn)的)。

考虑对这棵树树链剖分。显然一个节点会贡献给所有的祖先,那么考虑先处理一个节点的所有轻儿子,再处理重儿子,把重儿子的信息保留,轻儿子的删除,然后再暴力重新统计一遍轻儿子即可。

为什么是O(nlogn)?因为每个轻儿子的信息被删除或者加入当且仅当当前节点向其连了一条轻边,而链剖时一个节点到根只有O(logn)条轻边,所以可以做到O(nlogn)(说不定循环展开一波就上天了。。)

当然这个方法可以适用于很多处理子树信息的情况。所以可拓展性比较强,不过比较遗憾的是其不支持修改。

#include<bits/stdc++.h>typedef long long ll;using namespace std;inline int rd(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}inline void W(ll x){    static int buf[50];    if(!x){putchar('0');return;}    if(x<0){putchar('-');x=-x;}    while(x){buf[++buf[0]]=x%10;x/=10;}    while(buf[0])putchar(buf[buf[0]--]+'0');}const int N=1e5+50;int n,col[N],id[N],dfn[N],sze[N],son[N],ind;int g[N],nt[N*2],v[N*2],cnt[N],mx,ec;ll sum[N],ans[N];inline void adde(int x,int y){nt[++ec]=g[x];g[x]=ec;v[ec]=y;}inline void dfs(int x,int f){    sze[x]=1;dfn[x]=++ind;id[ind]=x;    for(int j=g[x];j;j=nt[j])        if(v[j]!=f){            dfs(v[j],x);            sze[x]+=sze[v[j]];            if(!son[x]||(sze[son[x]]<sze[v[j]]))son[x]=v[j];        }}inline void add(int c,ll &x){    sum[cnt[c]]-=c;    sum[++cnt[c]]+=c;;    if(mx==cnt[c])x+=c;    if(mx==cnt[c]-1)x=c,mx=cnt[c];}inline void del(int c){    sum[cnt[c]]-=c;    sum[--cnt[c]]+=c;    if(mx==cnt[c]+1&&!sum[mx])mx--;}inline void dfs2(int x,int f,bool keep){    for(int j=g[x];j;j=nt[j]){        if(v[j]!=son[x]&&v[j]!=f)dfs2(v[j],x,0);    }    if(son[x])dfs2(son[x],x,1),ans[x]=ans[son[x]];    add(col[x],ans[x]);    for(int j=g[x];j;j=nt[j]){        if(v[j]!=son[x]&&v[j]!=f){            for(int p=dfn[v[j]]+sze[v[j]]-1;p>=dfn[v[j]];p--)                add(col[id[p]],ans[x]);        }    }    if(!keep)        for(int p=dfn[x]+sze[x]-1;p>=dfn[x];p--)del(col[id[p]]);}int main(){    n=rd();    for(int i=1;i<=n;i++)col[i]=rd();    for(int i=1;i<n;i++){        int x=rd(),y=rd();        adde(x,y);adde(y,x);    }    dfs(1,0);    dfs2(1,0,1);    for(int i=1;i<=n;i++)W(ans[i]),putchar('\n');}
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 俄罗斯芭蕾 当代俄罗斯油画 俄罗斯游记 到俄罗斯留学费用 俄罗斯度假 俄罗斯研究生 俄罗斯沙拉酱 俄罗斯有什么特产 如何办理俄罗斯签证 俄罗斯地铁 俄罗斯模特 去俄罗斯攻略 去俄罗斯留学费用 俄罗斯红海参 俄罗斯黑巧克力 俄罗斯石油公司 俄罗斯文化旅游 俄罗斯航空电话 俄罗斯豪华旅游 俄罗斯贝戈士望远镜 俄罗斯签证怎么办理 俄罗斯北欧四国游 俄罗斯地理 发货俄罗斯 俄罗斯旅游价位 俄罗斯双认证 俄罗斯签证代理 畅游俄罗斯 俄罗斯公寓 俄罗斯师范大学 俄罗斯读研条件 俄罗斯签证所需材料 俄罗斯俄罗斯旅游 俄罗斯 餐厅 俄罗斯三个月签证 俄罗斯著名学校 俄罗斯留学费用一览表 俄罗斯签证资料 俄罗斯留学就业 国旅俄罗斯旅游 俄罗斯圣彼得堡时间