【HDU 4694】Important Sisters

Problem

传送门

一张图中有 n n n 个点,之间存在 m m m 个单向的信息传递关系,其中点 n n n 是所有消息的源头。

如果去掉点 b b b,点 n n n 就无法给点 a a a 传递信息,则称 b b b a a aimportant sister。

求每个点的所有 important sister 的编号之和。

数据范围: 1 ≤ n ≤ 5 × 1 0 4 1 ≤ n ≤ 5\times 10^4 1n5×104 0 ≤ m ≤ 1 0 5 0 ≤ m ≤ 10^5 0m105


Solution

不难发现,点 x x ximportant sister 就是点 x x x 的支配点。

于是我们构建出支配树,那么显然,我们要求的答案就是从根到 x x x 这一条链上的点编号之和。

其实这个和统计 s i z e size size 之类的差不多。(具体实现就看代码中 DfsTree

也是一道比较简单的题。

注意一下,由于有多组数据,一定要注意初始化。


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
int n,m,tot;
struct edge{
	int t,first[N],v[N],nxt[N];
	void add(int x,int y){
		nxt[++t]=first[x],first[x]=t,v[t]=y;
	}
}G,InvG,Semi,Idom;
int Set[N],Min[N],fa[N],dfn[N],pos[N],semi[N],idom[N],ans[N];
void Clear(){
	memset(ans,0,sizeof(ans));
	memset(dfn,0,sizeof(dfn));
	memset(G.first,0,sizeof(G.first));
	memset(InvG.first,0,sizeof(InvG.first));
	memset(Semi.first,0,sizeof(Semi.first));
	memset(Idom.first,0,sizeof(Idom.first));
	G.t=InvG.t=Semi.t=Idom.t=tot=0;
}
int find(int x){
	if(Set[x]==x)  return x;
	int father=find(Set[x]);
	if(dfn[semi[Min[Set[x]]]]<dfn[semi[Min[x]]])  Min[x]=Min[Set[x]];
	return Set[x]=father;
}
void dfs(int x){
	dfn[x]=++tot,pos[tot]=x;
	for(int i=G.first[x];i;i=G.nxt[i]){
		int to=G.v[i];
		if(dfn[to])  continue;
		fa[to]=x,dfs(to);
	}
}
void Tarjan(){
	for(int j=tot;j>1;--j){
		int x=pos[j];
		for(int i=InvG.first[x];i;i=InvG.nxt[i]){
			int to=InvG.v[i];
			if(!dfn[to])  continue;
			find(to);
			if(dfn[semi[Min[to]]]<dfn[semi[x]])  semi[x]=semi[Min[to]];
		}
		Semi.add(semi[x],x),Set[x]=fa[x];
		int now=fa[x];
		for(int i=Semi.first[now];i;i=Semi.nxt[i]){
			int to=Semi.v[i];find(to);
			if(semi[Min[to]]==now)  idom[to]=now;
			else  idom[to]=Min[to];
		}
	}
	for(int i=2;i<=tot;++i){
		int x=pos[i];
		if(idom[x]!=semi[x])  idom[x]=idom[idom[x]];
		Idom.add(idom[x],x);
	}
}
void DfsTree(int x){
	ans[x]+=x;
	for(int i=Idom.first[x];i;i=Idom.nxt[i]){
		int to=Idom.v[i];
		ans[to]+=ans[x],DfsTree(to);
	}
}
int main(){
	int x,y,i;
	while(~scanf("%d%d",&n,&m)){
		Clear();
		for(i=1;i<=n;++i)
			Set[i]=Min[i]=semi[i]=i;
		for(i=1;i<=m;++i){
			scanf("%d%d",&x,&y);
			G.add(x,y),InvG.add(y,x);
		}
		dfs(n),Tarjan();
		DfsTree(n);
		for(i=1;i<=n;++i){
			printf("%d",ans[i]);
			if(i!=n)  putchar(' ');
		}
		puts("");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值