模拟赛 :仙人球 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;
}

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