1. 程式人生 > >[黑科技]樹上啟發式合並 初步

[黑科技]樹上啟發式合並 初步

fin with def using sync 可能 樹鏈剖分 統計 reg

題意:給出\(n\)個節點的樹,每個節點有一種顏色,統計每棵子樹的不同顏色的數目

直接對每個樹\(dfs\)並回溯可以得出\(O(n^2)\)的算法,並不是十分OK
看了下Codeforces裏的Tutorial,暫時感受了一種叫做dsu on tree的暴力黑科技
核心就是進行樹鏈剖分,分出重兒子和輕兒子,每次離線\(dfs\)時保留重兒子得出的貢獻,清除輕兒子的貢獻並重新遍歷
可以證明是\(O(nlogn)\),證明方法沒看,應該是樹鏈剖分的同款證明?
以下試列出可能正確的代碼(沒地方交啊orz)

#include<iostream>
#include<algorithm>
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define rep(i,j,k) for(register int i=j;i<=k;i++) #define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i]) #define iin(a) scanf("%d",&a) #define lin(a) scanf("%lld",&a) #define din(a) scanf("%lf",&a) #define s0(a) scanf("%s",a) #define s1(a) scanf("%s",a+1) #define print(a) printf("%lld",(ll)a) #define enter putchar(‘\n‘) #define blank putchar(‘ ‘)
#define println(a) printf("%lld\n",(ll)a) #define IOS ios::sync_with_stdio(0) #define clr(a,b) memset(a,b,sizeof a) using namespace std; const int maxn = 1e5+11; const int oo = 0x3f3f3f3f; const double eps = 1e-7; typedef long long ll; ll read(){ ll x=0,f=1;register char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int to[maxn<<1],nxt[maxn<<1],head[maxn],tot; int sz[maxn],st[maxn],ed[maxn],pre[maxn],tot2; int cnt[maxn],col[maxn],C; void init(){ memset(head,-1,sizeof head); memset(cnt,0,sizeof cnt); tot=tot2=0; } void add(int u,int v){ to[tot]=v;nxt[tot]=head[u];head[u]=tot++; swap(u,v); to[tot]=v;nxt[tot]=head[u];head[u]=tot++; } void prepare(int u,int p){ sz[u]=1; st[u]=++tot2;pre[tot2]=u; erep(i,u){ int v=to[i]; if(v!=p){ prepare(v,u); sz[u]+=sz[v]; } } ed[u]=tot2; } void dfs(int u,int p,bool keep){ int mx=1,son=-1; erep(i,u){ int v=to[i]; if(v!=p&&sz[v]>mx){ mx=sz[v]; son=v; } } erep(i,u){ int v=to[i]; if(v==p||v==son)continue; dfs(v,u,0); } if(~son) dfs(son,u,1); erep(i,u){ int v=to[i]; if(v==p||v==son)continue; rep(i,st[v],ed[v]) cnt[col[pre[i]]]++; } cnt[col[u]]++; cout<<"Subtree "<<u<<endl; rep(i,1,C){ if(cnt[i]>0) cout<<"Color "<<i<<" has "<<cnt[i]<<" vertices"<<endl; } cout<<endl; if(!keep) rep(i,st[u],ed[u]) cnt[col[pre[i]]]--; } int main(){ int n,m; while(cin>>n>>C){ init();m=n-1; rep(i,1,n) col[i]=read(); rep(i,1,m){ int u=read(); int v=read(); add(u,v); } prepare(1,0); rep(i,1,n) cout<<i<<" "<<sz[i]<<" "<<st[i]<<" "<<ed[i]<<endl; cout<<endl; rep(i,1,tot2) cout<<i<<" "<<pre[i]<<endl; dfs(1,0,0); } return 0; }

給出一組樣例
其中\(n\)是節點個數,\(C\)是顏色總數,\(col[i]\)為第\(i\)個節點的顏色

8 3
1 2 1 2 3 1 2 1
1 2
1 5
1 8
2 3
2 4
5 6
6 7

[黑科技]樹上啟發式合並 初步