P2597 [ZJOI2012]灾难

想办法把这些点搞成一棵树,然后答案就是以该点为根的子树的大小。怎么搞呢?

把这些点的拓扑序搞出来,然后依次进行处理:

①如果当前点只有一个父亲,那么直接挂在它的父亲上

②如果当前点不止一个父亲,那么对这些父亲求lca,然后挂到这个lca上。

一开始程序一直无法运行,后来才知道,vector虽然资瓷删除指定元素,但由于它是动态开点的,无法提前记录指定元素的位置(push_back后会发生改变)。因此还是乖乖用set,反正时间复杂度还是对的。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set> 
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define pvi pair<vector<int>::iterator,int>
#define mkp make_pair
#define X first
#define Y second
#define LL long long
const int N=70005;
int n;vector<int>pre[N];
set<int>he[N];
int dep[N],dad[N][20],cs;
int q[N],cnt[N];
int sz[N];
int lca(int x,int y){
	int t;
	if(dep[x]<dep[y])swap(x,y);
	per(t,cs,0)if(dep[dad[x][t]]>=dep[y])x=dad[x][t];
	if(x==y)return x;
	per(t,cs,0)if(dad[x][t]!=dad[y][t])x=dad[x][t],y=dad[y][t];
	return dad[x][0];
}
void BFS(){
	int Ft=0,Rr=1,u,v,fa,t;
	vector<int>::iterator ii;
	set<int>::iterator jj;
	dep[n+1]=1;
	for(q[0]=n+1;Ft<Rr;){
		if((u=q[Ft++])<=n){
			fa=*pre[u].begin();
			for(ii=++pre[u].begin();ii!=pre[u].end();++ii)
				fa=lca(*ii,fa);
			for(ii=pre[u].begin();ii!=pre[u].end();++ii)
				he[*ii].erase(he[*ii].lower_bound(u));
			he[fa].insert(u);
			dep[u]=dep[dad[u][0]=fa]+1;
			for(t=1;v=dad[dad[u][t-1]][t-1];++t)
				dad[u][t]=v,cs=max(cs,t);
		}
		for(jj=he[u].begin();jj!=he[u].end();++jj)if(!(--cnt[*jj]))
			q[Rr++]=*jj;
	}
}
void DFS(int x,int fa){
	set<int>::iterator ii;sz[x]=1;
	for(ii=he[x].begin();ii!=he[x].end();++ii){
		DFS(*ii,x);sz[x]+=sz[*ii];
	}
}/*
void write(){
	puts("pre");
	int i;
	vector<int>::iterator ii;
	set<int>::iterator jj;
	rep(i,1,n){
		for(ii=pre[i].begin();ii!=pre[i].end();++ii)
			printf("%d ",*ii);
		puts("");
	}
	puts("he");
	rep(i,1,n){
		for(jj=he[i].begin();jj!=he[i].end();++jj)
			printf("%d ",*jj);
		puts("");
	}
	puts("");
}*/
int main(){
	int i,x;
	scanf("%d",&n);
	rep(i,1,n){
		while(1){
			scanf("%d",&x);
			if(!x)break;
			he[x].insert(i);
			pre[i].push_back(x);
		}
		if(pre[i].empty()){
			he[n+1].insert(i);
			pre[i].push_back(n+1);
		}
		cnt[i]=pre[i].size();
	}
//	write();
	BFS();DFS(n+1,0);
	rep(i,1,n)printf("%d\n",sz[i]-1);
	return 0;
} 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值