【BZOJ】2815[ZJOI2012]灾难-灭绝树/支配树

传送门:bzoj2815
支配树模板


题解

先拓扑排序,建立灭绝树,对于该树,满足如下定义:
对于树中的每个节点,若该节点的生物灭绝,那么以它为跟的子树内的所有节点的生物都会跟着灭绝。
按拓扑序倒序加入“灭绝树”,建的时候连在所有它的食物点的lca下即可(lca灭绝代表,所有它可以吃的都灭绝了)。


代码

#include<bits/stdc++.h>
#define gc getchar()
#define pb push_back
using namespace std;
const int N=1e5+10;

int in[N],head[N],to[N],nxt[N],tot,sz[N];
int n,cnt,bin[22],f[N][21],dep[N],id[N];
vector<int>g[N];

char cp;
inline void rd(int &x)
{
	cp=gc;x=0;int f=0;
	for(;!isdigit(cp);cp=gc) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;in[v]++;}

inline int LCA(int x,int y)
{
	if(x==-1) return y;
	if(dep[x]<dep[y]) swap(x,y);
	int i,dlt=dep[x]-dep[y];
	for(i=0;bin[i]<=dlt;i++) if(bin[i]&dlt)
		x=f[x][i];
	if(x==y) return x;
	for(i=20;i>=0;--i)
		if(f[x][i]!=f[y][i])
		  x=f[x][i],y=f[y][i];
	return f[x][0];
}

void dfs(int x)
{
	int i,j;
	for(i=g[x].size()-1;i>=0;--i){
		j=g[x][i];dfs(j);
		sz[x]+=(sz[j]+1);
	}
}

queue<int>que;
int main(){
	int i,j,x,y,z;
	bin[0]=1;for(i=1;i<=21;i++) bin[i]=bin[i-1]<<1;
	rd(n);
	for(i=1;i<=n;i++)
		for(rd(x);x;rd(x)) lk(i,x);
    for(i=1;i<=n;i++) if(!in[i]) que.push(i);
    for(;!que.empty();){
    	x=que.front();que.pop();id[++cnt]=x;
    	for(i=head[x];i;i=nxt[i]){
    		j=to[i];in[j]--;if(!in[j]) que.push(j);
		}
	}
	for(j=cnt;j>0;--j){
		x=id[j];y=-1;//注意y一定要设成-1 而不是0,因为0也是灭绝树上的点 
		for(i=head[x];i;i=nxt[i]) y=LCA(y,to[i]);
		if(y==-1) y=0;g[y].pb(x);dep[x]=dep[y]+1;f[x][0]=y;
		for(i=1;bin[i]<=dep[x];++i) f[x][i]=f[f[x][i-1]][i-1];
	}
	dfs(0);
	for(i=1;i<=n;++i) printf("%d\n",sz[i]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值