浅谈DAG上的支配树
当树也拥有了很强的控制欲……
[ZJOI2012] 灾难
支配树顾名思义,就是一棵树,这棵树上的节点的父亲对儿子起支配作用(家庭地位),如果父节点没了,儿子节点也没了。
对于这道题,我们可以设生物 \(u\) 对 \(v\) 起支配作用,如果 \(u\) 灭绝了,那么 \(v\) 也会灭绝。那这样,题目所求的灾难值就是 \(i\) 在支配树上的子树大小减一(除自己)。
考虑如何建这棵树。
我们先跑一边拓扑,找出拓扑序。然后按照拓扑序倒序找(从生产者开始找),这个节点的支配点就是她所有相连的点的 \(lca\),加边即可。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=1e6+5,INF=2e9;
int n;
int hd1[N],nx1[N],to1[N],tot1,in[N];
void adde1(int u,int v){
nx1[++tot1]=hd1[u];to1[tot1]=v;hd1[u]=tot1;in[v]++;
}
int hd[N],nx[N],to[N],tot;
void adde(int u,int v){
nx[++tot]=hd[u];to[tot]=v;hd[u]=tot;
}
int f[N][25],dep[N];
int LCA(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
int tpn[N],cnt;
void topu(){
queue<int> q;
for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
tpn[++cnt]=u;
for(int i=hd1[u];i;i=nx1[i]){
int v=to1[i];
in[v]--;
if(!in[v]) q.push(v);
}
}
}
void build(){
dep[n+1]=1;
for(int i=cnt;i;i--){
int u=tpn[i];
if(!hd1[u]){
adde(n+1,u);
f[u][0]=n+1;
dep[u]=2;
continue;
}
int lca=to1[hd1[u]];
for(int i=nx1[hd1[u]];i;i=nx1[i])
lca=LCA(lca,to1[i]);
f[u][0]=lca;
dep[u]=dep[lca]+1;
adde(lca,u);
for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
}
}
int sz[N];
void dfs(int u,int father){
sz[u]=1;
for(int i=hd[u];i;i=nx[i]){
int v=to[i];
if(v==father) continue;
dfs(v,u);
sz[u]+=sz[v];
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
while(1){
int x=read();
if(x==0) break;
adde1(i,x);
}
}
topu();
build();
dfs(n+1,0);
for(int i=1;i<=n;i++) printf("%d\n",sz[i]-1);
return 0;
}

浙公网安备 33010602011771号