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一定比他大
所以,對於出現了
dfnxlowsondfn_x \le low_{son}
在這裏插入圖片描述
返祖邊只能是在son的子樹和x中指來指去。
無法到達x以上的點,即x是割點。
至於橫叉邊,是絕不可能出現的,因爲在DFS中,所謂“橫叉邊”會當成樹邊走掉。

回到原題

至於原題,
滿足dfnxlowsondfn_x \leqslant low_{son}的,顯然
SxS_x是以x爲根的子樹的節點數
ansx=isondfnxlowiSi(nSi1)+n1a 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
這是會有重複的,感性理解一下,不解釋了
重複的部分是
12[(isondfnxlowiSi)2isondfnxlowiSi2]\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]
所以
ansx=isondfnxlowiSi(nSi1)12[(isondfnxlowiSi)2isondfnxlowiSi2]+n1a 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
(注:我的代碼中,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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章