模擬賽 :仙人球 tarjan縮點+倍增

仙人球

cactus.cpp

題目描述

如果一個無自環無重邊無向連通圖的任意一個點最多屬於一個簡單環,我們就稱之爲仙人球。所謂簡單環即不經過重複的結點的環。

現在,小Z有一張仙人球圖,他想知道兩個點之間不同的非複雜路徑的條數。這裏複雜路徑指的是經過某條邊至少兩次的路徑,兩條路徑不同當且僅當它們之中的邊不同。這個問題太難了,所以小Z想要請教請教你。當然,爲了不讓你特別尷尬,你只需要求出條數mod 1000000007的值即可

 

輸入格式:

1行:兩個整數:n、m,分別代表仙人球圖的點數和邊數

2~m+1行:每行兩個整數xi、yi,表示xi和yi之間有一條邊

m+2行:一個整數k,表示詢問個數

m+3~m+k+2行:每行兩個整數ai、bi,表示小G的詢問

 

輸出格式:

樣例輸入

樣例輸出

10 11

1 2

2 3

3 4

1 4

3 5

5 6

8 6

8 7

7 6

7 9

9 10

6

1 2

3 5

6 9

9 2

9 3

9 10

2

2

2

4

4

1

 

輸出k行,每行一個整數,爲ai到bi的不同的非複雜路徑的條數mod 1000000007的值。

 

 

 

 

 

 

樣例解釋

畫圖易看出詢問1、3、4、6的答案;詢問2有兩種方案:3->5和3->4->1->2->3->5,這裏應注意3->4->1->2->3->5和3->2->1->4->3->5是同一條路徑;詢問5同理。

 

數據範圍與約定

對於15%的數據,n、m、k≤30

對於40%的數據,n、m、k≤500

對於70%的數據,n、m、k≤2000

對於100%的數據,n、k≤100000,m≤150000,數據保證給出的是一張仙人球圖,保證每個詢問的ai≠bi。


題解:本題看似神數據結構仙人掌,其實關係不大,僅僅是借用了仙人掌的簡單性質,我們可以認爲仙人掌上的簡單環對於解本題提供的難度是,從一個點到另一個點,若有簡單環,則從左右出發各一條,即每多一個環,則路線數翻倍,很好理解吧,可不可以說是乘法原理QAQ,然後題就是把仙人掌上的簡單環縮點,然後詢問路徑上有多少個環,最後做一個快速冪就可以了,然而思想很簡單,我碼力好弱啊QAQ,抄了STD。

貼上代碼:

#include <cstdio>
#include <algorithm>
#define N 200010
using namespace std;
int head[N][2],to[N][2],next1[N][2],cnt[2],dd[N],low[N],tot,vis[N],ins[N],sta[N],top,bel[N],num;
int fa[N][22],deep[N],sum[N][22],log1[N],pow1[N];
void add(int p , int x , int y)
{
	to[++cnt[p]][p] = y , next1[cnt[p]][p] = head[x][p] , head[x][p] = cnt[p];
}
void tarjan(int x , int from)
{
	int i , t;
	dd[x] = low[x] = ++tot , vis[x] = ins[x] = 1 , sta[++top] = x;
	for(i = head[x][0] ; i ; i = next1[i][0])
	{
		if(to[i][0] == from) continue;
		if(!vis[to[i][0]]) tarjan(to[i][0] , x) , low[x] = min(low[x] , low[to[i][0]]);
		else if(ins[to[i][0]]) low[x] = min(low[x] , dd[to[i][0]]);
	}
	if(dd[x] == low[x])
	{
		num++;
		do
		{
			t = sta[top -- ] , ins[t] = 0 , bel[t] = num;
			if(t != x) sum[num][0] = 1;	//如果這個點不是隻有自己  就標記在環中 
		}while(t != x);
	}
}
void dfs(int x)
{
	int i;
	for(i = 1 ; i <= log1[deep[x]] ; i ++ ) 
		fa[x][i] = fa[fa[x][i - 1]][i - 1] , sum[x][i] = sum[x][i - 1] + sum[fa[x][i - 1]][i - 1];
	for(i = head[x][1] ; i ; i = next1[i][1]) if(to[i][1] != fa[x][0]) 
		fa[to[i][1]][0] = x , deep[to[i][1]] = deep[x] + 1 , dfs(to[i][1]);
}
int cal(int x , int y)
{
	int i , ans = 0;
	if(deep[x] < deep[y]) swap(x , y);
	for(i = log1[deep[x] - deep[y]] ; i >= 0 ; i -- ) 
		if(deep[x] - deep[y] >= (1 << i)) ans += sum[x][i] , x = fa[x][i];
	for(i = log1[deep[x]] ; i >= 0 ; i -- ) 
		if(fa[x][i] != fa[y][i]) ans += sum[x][i] + sum[y][i] , x = fa[x][i] , y = fa[y][i];
	if(x != y) ans += sum[x][0] + sum[y][0] , x = fa[x][0];
	return ans + sum[x][0];
}
int main()
{
	int n , m , k , i , x , y;
	scanf("%d%d" , &n , &m);
	while(m -- ) scanf("%d%d" , &x , &y) , add(0 , x , y) , add(0 , y , x);
	tarjan(1 , 0);
	for(x = 1 ; x <= n ; x ++ ) for(i = head[x][0] ; i ; i = next1[i][0])
	if(bel[x] != bel[to[i][0]]) add(1 , bel[x] , bel[to[i][0]]);  
	pow1[0] = 1 , pow1[1] = 2;
	for(i = 2 ; i <= n ; i ++ ) log1[i] = log1[i >> 1] + 1 , pow1[i] = pow1[i - 1] * 2 % 1000000007;
	dfs(1);//重構
	scanf("%d" , &k);
	while(k -- ) scanf("%d%d" , &x , &y) , printf("%d\n" , pow1[cal(bel[x] , bel[y])]);
	return 0;
}

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