JZOJ 3896 战争游戏——Tarjan割点

战争游戏

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

7 9
1 2
1 3
1 4
1 5
1 6
1 7
2 3
4 5
6 7

Sample Output

18
6
6
6
6
6
6

Data Constraint

在这里插入图片描述
这题在一定程度上是挺水的,我拿它讲一讲tarjan求割点。

定义

引用百度百科

设G是一个图,v是G的一个顶点,如果G-v的连通分支数大于G的连通分支数,则称v是G的一个割点引用百度百科

简单的说,割点就是那种拿掉了,图就不连通了的点。

Tarjan

tarjan是很多算法的总称,都由tarjan发明,
主要有 求强连通分量,双联通分量的,求Lca的。
也可以求割点

引入概念

Tarjan中有两个很重要的数组——low , dfn
dfn是指递归时,节点的dfs序,而low数组,
则是该节点与其子树的节点通过返祖边可到达的节点的dfn的最小值。
:我的程序中,该节点到父亲的边也算作返祖边)
在这里插入图片描述
如图,点D与A之间有一条返祖边,所以节点D的low=1.

求割点

首先对于点x,它的所有子树节点,dfn一定比他大
所以,对于出现了
d f n x ≤ l o w s o n dfn_x \le low_{son} dfnxlowson
在这里插入图片描述
返祖边只能是在son的子树和x中指来指去。
无法到达x以上的点,即x是割点。
至于横叉边,是绝不可能出现的,因为在DFS中,所谓“横叉边”会当成树边走掉。

回到原题

至于原题,
满足 d f n x ⩽ l o w s o n dfn_x \leqslant low_{son} dfnxlowson的,显然
S x S_x Sx是以x为根的子树的节点数
a n s x = ∑ i ∈ s o n d f n x ⩽ l o w i S i ( n − S i − 1 ) + n − 1 a n s _ { x } = \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } \left( n - S _ { i } - 1 \right)+ n - 1 ansx=isondfnxlowiSi(nSi1)+n1
这是会有重复的,感性理解一下,不解释了
重复的部分是
1 2 [ ( ∑ i ∈ s o n d f n x ⩽ l o w i S i ) 2 − ∑ i ∈ s o n d f n x ⩽ l o w i S i 2 ] \frac { 1 } { 2 } \left[ \left( \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } \right) ^ { 2 } - \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } ^ { 2 } \right] 21[(isondfnxlowiSi)2isondfnxlowiSi2]
所以
a n s x = ∑ i ∈ s o n d f n x ⩽ l o w i S i ( n − S i − 1 ) − 1 2 [ ( ∑ i ∈ s o n d f n x ⩽ l o w i S i ) 2 − ∑ i ∈ s o n d f n x ⩽ l o w i S i 2 ] + n − 1 a n s _ { x } = \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } \left( n - S _ { i } - 1 \right) - \frac { 1 } { 2 } \left[ \left( \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } \right) ^ { 2 } - \sum _ { i \in s o n } ^ { d f n _ { x } \leqslant l o w _ { i } } S _ { i } ^ { 2 } \right] + n - 1 ansx=isondfnxlowiSi(nSi1)21[(isondfnxlowiSi)2isondfnxlowiSi2]+n1
(注:我的代码中,dfm就是low)

#include<cstdio>
#include<cstring>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
using namespace std;
int to[200010],next[200010],last[50010],con=0;
int dfn[50010],dfm[50010],sn[50010],dfnn=0;
int fa[50010],f[50010];
int stack[50010],top=0;
int ans[50010],n;
void tarjan(int x)
{
	dfn[x]=dfm[x]=++dfnn;
	stack[++top]=x;
	int a2=0,sa=0;
	sn[x]=1;int i;
	for(i=last[x];i>0;i=next[i])
	{
		if(dfn[to[i]]==0)
		{
			tarjan(to[i]);
			dfm[x]=min(dfm[x],dfm[to[i]]);
			sn[x]+=sn[to[i]];
			if(dfn[x]<=dfm[to[i]])
			{
				ans[x]+=(n-sn[to[i]]-1)*sn[to[i]];
				sa+=sn[to[i]]*sn[to[i]];
				a2+=sn[to[i]];
				if(dfm[stack[top]]==x)
				{
					do
					{
						top--;
					}while(stack[top]!=x);
				}
			}
		}
		else dfm[x]=min(dfm[x],dfn[to[i]]);
	}
	ans[x]-=(a2*a2-sa)/2;
}
int main()
{
	memset(sn,0,sizeof(sn));
	memset(last,0,sizeof(last));
	memset(dfn,0,sizeof(dfn));
	memset(dfm,0,sizeof(dfm));
	int m,i,u,v;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		con++;to[con]=v;next[con]=last[u];last[u]=con;
		con++;to[con]=u;next[con]=last[v];last[v]=con;
	}
	tarjan(1);
	for(i=1;i<=n;i++) 
	for(i=1;i<=n;i++) printf("%d\n",ans[i]+n-1);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值