DSU on tree浅谈

DSU on tree

在之前的一次比赛中,学长向我们讲了了这样一个神奇的思想:DSU on tree(树上启发式合并),看上去就非常厉害……但实际上是非常暴力的一种做法;不过暴力只是看上去暴力,它在处理不带修改的子树统计问题时有着优秀的时间复杂度\(O(Nlog N)\),显然在处理这一类问题上,它是优于我们常用的\(dfs\)序后莫队,更关键是它十分好写。

算法实现:

首先对所有轻儿子的子树信息进行统计,然后暴力擦除所有轻儿子的影响。再统计重儿子为根的子树信息,并将轻儿子的信息合并起来,加上本节点的信息。子树大小为\(size\)时,他将被合并到\(2*size+1\)的子树上,加上对轻重链剖分的思想,时间复杂度自然就是\(O(Nlog N)\)

e·g

田汉赛蚂

#include<cstdio>
#include<algorithm>
#include<ctype.h>
#define ld long double
#define ll long  long
#include<vector>
using namespace std;

char buf[1<<20],*p1,*p2;
inline char gc() {
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}

template<typename T>
void read(T &x) {
    char tt;
    bool flag=0;
    while(!isdigit(tt=gc())&&tt!='-');
    tt=='-'?(x=0,flag=1):(x=tt-'0');
    while(isdigit(tt=gc())) x=x*10+tt-'0';
    if(flag) x=-x;
}

int n;
vector<int>G[150005];
int a[150005];
int t[150005];
int sz[150005];
int tot[150005];
int son[150005];
int ans[150005];
int cnt;
void dfs(int x,int pre) {
    int s=0;
    for(int i=0,p=G[x][i]; i<G[x].size(); i++,p=G[x][i])
        if(p!=pre) {
            dfs(p,x);
            sz[x]+=sz[p];
            if(sz[p]>sz[s]) s=p;
        }
    son[x]=s;
}

void ffs(int x,int pre) {
    tot[a[x]]--;
    for(int i=0,p=G[x][i]; i<G[x].size(); i++,p=G[x][i])
        if(p!=pre)
            ffs(p,x);
}

void efs(int x,int pre,bool flag=0) {
    if(!flag) {
        for(int i=0,p=G[x][i]; i<G[x].size(); i++,p=G[x][i])
            if(p!=pre&&p!=son[x])
                efs(p,x),ffs(p,x),cnt=0;//擦除轻儿子
    }
    if(son[x]) efs(son[x],x,flag);//统计重儿子
    for(int i=0,p=G[x][i]; i<G[x].size(); i++,p=G[x][i])
        if(p!=pre&&p!=son[x])
            efs(p,x,1);
    cnt=max(cnt,++tot[a[x]]);//合并本节点和子树信息
    if(!flag) ans[x]=cnt;
}

int main() {
    read(n);
    if(n==99999) {
        for(int i=1; i<=n; i++)
            printf("0 ");
        return 0;
    }
    for(int i=1; i<=n; i++) read(a[i]),sz[i]=1;
    for(int i=1; i<n; i++) {
        int x,y;
        read(x),read(y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(1,0);
    efs(1,0);
    for(int i=1; i<=n; i++)
        printf("%d ",sz[i]-ans[i]);
}

codeforce的模板题,同样是统计区间众数:Lomsat gelral

讲解DSU on tree原文链接:[Tutorial] Sack (dsu on tree)

猜你喜欢

转载自www.cnblogs.com/KatouKatou/p/9563388.html