[BZOJ2815][ZJOI2012]灾难(拓扑排序/支配树)

支配树目前只见到这一个应用,那就不独分一类,直接作为拓扑排序题好了。

每个点向所有食物连边,定义fa[x]为x的支配点,即离x最近的点,满足若fa[x]灭绝,则x也要灭绝。

这样,将fa[x]向x连边,则建出的新图是一棵树,这就是支配树(不是严谨的支配树,被出题人称为灭绝树)

建树流程是,将拓扑序反向,对于一个点x,求它的出点在新图(树)中的LCA,然后将这个LCA作为fa[x]即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 7 typedef long long ll;
 8 using namespace std;
 9 
10 const int N=100010;
11 int n,x,sz[N],dep[N],d[N],fa[N][18],q[N];
12 
13 int lca(int u,int v){
14     if (u==-1) return v;
15     if (dep[u]<dep[v]) swap(u,v);
16     int t=dep[u]-dep[v];
17     for (int i=16; ~i; i--) if (t&(1<<i))  u=fa[u][i];
18     if (u==v) return u;
19     for (int i=16; ~i; i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
20     return fa[u][0];
21 }
22 
23 struct Graph{
24     int cnt,h[N],to[N<<1],nxt[N<<1];
25 
26     void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
27 
28     void Top(){
29         int st=0,ed=0;
30         rep(i,1,n) if (!d[i]) q[++ed]=i;
31         while (st<ed){
32             int x=q[++st];
33             For(i,x){ d[k=to[i]]--; if (!d[k]) q[++ed]=k; }
34         }
35     }
36 
37     void ext(int x,int y){
38         add(x,y); dep[y]=dep[x]+1; fa[y][0]=x;
39         rep(i,1,16) fa[y][i]=fa[fa[y][i-1]][i-1];
40     }
41 
42     void dfs(int x){ sz[x]=1; For(i,x) dfs(k=to[i]),sz[x]+=sz[k]; }
43 }G1,G2;
44 
45 void build(){
46     for (int i=n; i; i--){
47         int x=q[i],tmp=-1;
48         for (int i=G1.h[x]; i; i=G1.nxt[i]) tmp=lca(tmp,G1.to[i]);
49         G2.ext(max(tmp,0),x);
50     }
51 }
52 
53 int main(){
54     freopen("bzoj2815.in","r",stdin);
55     freopen("bzoj2815.out","w",stdout);
56     scanf("%d",&n);
57     rep(i,1,n){
58         for (scanf("%d",&x); x; scanf("%d",&x)) G1.add(i,x),d[x]++;
59     }
60     G1.Top(); build(); G2.dfs(0);
61     rep(i,1,n) printf("%d\n",sz[i]-1);
62     return 0;
63 }

猜你喜欢

转载自www.cnblogs.com/HocRiser/p/10143148.html