洛谷P3469 [POI2008]BLO-Blockade——Tarjan求割点(需对Tarjan求割点的过程的清晰认识)

题目:https://www.luogu.org/problemnew/show/P3469

分析:

1、用前向星存图,然后按边逆序进行搜索。

2、需要对Tarjan判断割点有条件深刻的认识。

先看代码:if( (u==root&&cnt>1) || (u!=root&&dfn[u]<=low[v])u为割点。

对于下面的图而言:

 

按边(1,2),(1,3),(1,4)用前向星存图,以结点1为root遍历边,得到的dfs序列是:1->4->3->2

因为1->4时,dfn[1]=low[1]=1,dfn[4]=low[4]=2,将都不满足条件u!=root与dfn[u]<=low[v],从而得不出结点root=1为割点。

当2->3时,dfn[3]=low[3]=3,将得出1是割点,此时连通块{3}的size[3]=1,从而由3连向{2,4}的对数有1*2=2;

当3->2时,dfn[2]=low[2]=4,仍将得出1是割点,此时连通块{2}的size[2]=1,从而由2连向{3,4}的对数有1*2=2;

现在回到连通块{4},由4连向{2,3}的对数有(4-2-1)*2=2。左边第二项的2=size[3}+size[2]。

再看下图:

 

以1为root。搜索顺序为1->2-3->4->5->6。

由(1,2),(2,3)得不出2是割点,但由(2,4)可以得出2是割点。

同样的,由(2,4)得不出4是割点,但由(4,5),(4,6)都可以得出4是割点。

对于点2而言,ans=size[4]*(n-size[4]-1)+(n-sum-1)*sum+2*(n-1),其中的sum=size[4]。

由上面两个图,要说明的是:

如果点u是割点,则至少有两条边与u相连,其中有一条边得不出u是割点,这在编程是要引起高度重视。

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int MaxN=1e5+5,MaxM=10e5+5;

inline int get(){//读入优化 
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	return x;
}

int n,m,root,tot,head[MaxM],to[MaxM],from[MaxM],nex[MaxM];
long long ans[MaxN];
void join(int x,int y){
	nex[++tot]=head[x];
	head[x]=tot;
	to[tot]=y;
	from[tot]=x;
}

int num,dfn[MaxN],low[MaxN],size[MaxN],cut[MaxN];
void Tarjan(int u){
	dfn[u]=low[u]=++num;
	size[u]=1;
	int cnt=0,sum=0;
	for(int i=head[u];i;i=nex[i]){
		int v=to[i];
		if(!dfn[v]){
			cnt++;
			Tarjan(v);
			size[u]+=size[v];
			low[u]=min(low[u],low[v]);
			if( (u==root&&cnt>1)
			|| (u!=root&&dfn[u]<=low[v]) ){
				cut[u]=1;//标记u是割点
				ans[u]+=(long long)(n-size[v]-1)*size[v];
				sum+=size[v];//难点在这里!!
			}	
		}
		else low[u]=min(low[u],dfn[v]);
	}
	if(!cut[u])ans[u]=2*(n-1);
	if(cut[u])ans[u]+=(long long)(n-sum-1)*sum+2*(n-1);	//难点在这里!!
}
int main(){
	n=get();m=get();
	int x,y;
	for(int i=1;i<=m;i++){
		x=get();y=get();
		join(x,y);join(y,x);
	}
	
	root=1;
	Tarjan(1);//Tafjan求割点,同时求出答案
	
	for(int i=1;i<=n;i++)
		printf("%lld\n",ans[i]);
		
	 return 0; 
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值