#线段树合并树上启发式合并#CF600E Lomsat gelral

Posted spare-no-effort

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#线段树合并树上启发式合并#CF600E Lomsat gelral相关的知识,希望对你有一定的参考价值。

题目

一棵树有(n)个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和


分析1

线段树合并,记录(w,sum)分别表示编号和以及颜色和,当颜色和相同时两个编号都要加,否则只加大的那一个,时间复杂度(O(nlog_2n))


代码1

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; long long ans[N];
struct xds{int ls,rs,sum; long long w;}h[N<<5];
struct node{int y,next;}e[N<<1];
int col[N],hs[N],root[N],cnt,k=1,n;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(long long ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline void pup(int rt){
    if (h[h[rt].ls].sum>h[h[rt].rs].sum)
        h[rt].sum=h[h[rt].ls].sum,h[rt].w=h[h[rt].ls].w;
    else h[rt].sum=h[h[rt].rs].sum,h[rt].w=h[h[rt].rs].w;
    if (h[h[rt].ls].sum==h[h[rt].rs].sum) h[rt].w+=h[h[rt].ls].w;
}
inline void update(int &rt,int l,int r,int x){
    if (!rt) rt=++cnt;;
    if (l==r) {h[rt].w=l,++h[rt].sum; return;}
    rr int mid=(l+r)>>1;
    if (x<=mid) update(h[rt].ls,l,mid,x);
        else update(h[rt].rs,mid+1,r,x);
    pup(rt);
}
inline void merge(int nrt,int lrt,int l,int r){
    if (l==r){
        h[nrt].w=l,h[nrt].sum+=h[lrt].sum;
        return;
    } 
    rr int mid=(l+r)>>1;
    if (h[lrt].ls){
        if (!h[nrt].ls) h[nrt].ls=h[lrt].ls;
            else merge(h[nrt].ls,h[lrt].ls,l,mid);
    }
    if (h[lrt].rs){
        if (!h[nrt].rs) h[nrt].rs=h[lrt].rs;
            else merge(h[nrt].rs,h[lrt].rs,mid+1,r);
    }
    pup(nrt);
}
inline void dfs(int x,int fa){
    for (rr int i=hs[x];i;i=e[i].next)
    if (e[i].y!=fa){
        dfs(e[i].y,x);
        merge(root[x],root[e[i].y],1,n);//合并子树
    }
    update(root[x],1,n,col[x]);//增加颜色
    ans[x]=h[root[x]].w;
}
signed main(){
    n=iut();
    for (rr int i=1;i<=n;++i) col[i]=iut(),root[i]=++cnt;//每个点构一棵线段树
    for (rr int i=1;i<n;++i){
        rr int x=iut(),y=iut();
        e[++k]=(node){y,hs[x]},hs[x]=k,
        e[++k]=(node){x,hs[y]},hs[y]=k; 
    }
    dfs(1,0);
    for (rr int i=1;i<=n;++i)
        print(ans[i]),putchar(i==n?10:32);
    return 0;
}

分析2

树上启发式合并,自底向上处理,对于子树只处理重儿子的情况,对于轻儿子统计完就清除信息,合并到父节点时才重新算一遍,除了树上数颜色,这应该是也是一道模板题吧,因为重儿子所在的子树超过子树节点的一半,所以时间复杂度应该为(O(nlog_2n)),树链剖分就是用了这个性质再加上线段树、树状数组的数据结构只是再多了一个(log_2n)


代码2

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; long long ans[N],now;
struct node{int y,next;}e[N<<1];
int col[N],hs[N],k=1,n,mx,cnt[N],root,dep[N],fat[N],son[N],big[N];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(long long ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline void dfs1(int x,int fa){
    dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
    for (rr int i=hs[x],mson=-1;i;i=e[i].next)
    if (e[i].y!=fa){
        dfs1(e[i].y,x);
        son[x]+=son[e[i].y];
        if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];//处理重儿子
    }
}
inline void update(int x,int z){//很好理解呀
    cnt[col[x]]+=z;
    if (cnt[col[x]]>mx) mx=cnt[col[x]],now=col[x];
        else if (cnt[col[x]]==mx) now+=col[x];
    for (rr int i=hs[x];i;i=e[i].next)
    if (e[i].y!=fat[x]&&e[i].y!=root) update(e[i].y,z);
}
inline void dfs2(int x,int opt){
    for (rr int i=hs[x];i;i=e[i].next)
    if (e[i].y!=fat[x]&&e[i].y!=big[x]) dfs2(e[i].y,0);
    if (big[x]) dfs2(big[x],1),root=big[x];
    update(x,1),ans[x]=now,root=0;
    if (!opt) update(x,-1),now=mx=0;
}
signed main(){
    n=iut();
    for (rr int i=1;i<=n;++i) col[i]=iut();
    for (rr int i=1;i<n;++i){
        rr int x=iut(),y=iut();
        e[++k]=(node){y,hs[x]},hs[x]=k,
        e[++k]=(node){x,hs[y]},hs[y]=k; 
    }
    dfs1(1,0),dfs2(1,0);
    for (rr int i=1;i<=n;++i)
        print(ans[i]),putchar(i==n?10:32);
    return 0;
}

以上是关于#线段树合并树上启发式合并#CF600E Lomsat gelral的主要内容,如果未能解决你的问题,请参考以下文章

cf600E. Lomsat gelral(树上启发式合并)

CF600E Lomsat gelral(树上启发式合并)

cf375D. Tree and Queries(树上启发式合并+线段树)

CF600E Lomsat gelral(树上启发式合并)

CF600E Lomsat gelral(线段树合并)

cf600 E. Lomsat gelral