DSU on Tree浅谈

it2022-05-05  116

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)

转载于:https://www.cnblogs.com/KatouKatou/p/9563388.html

相关资源:各显卡算力对照表!

最新回复(0)