Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

Codeforces #143 Div.2 E - Cactus

問題文 : Cactus

解法 :
答えは2^(パスs -> tまでの中にあるサイクルの数)になる(図を描いて考察すると分かる.)
よって、サイクルを検出して、それを1つの頂点に圧縮する.

頂点に圧縮する際に、根付き木にグラフを再構築すると, sとtのLCAをpとしておき
s->pのサイクルの数+t->pのサイクルの数が求めるサイクルの数になるので、前準備をしておけば,各クエリにlogN時間で応答することが出来る.

ゆえに, O((M+Q)logN)時間でプログラムが動作する.

※閉路検出などのテクニックは@kyuridenamidaさんに教わりました. この場を借りてお礼を申し上げます.

コード :

#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;

#define MAX_N (100000)
#define MAX_M (100000)

struct Edge {
	int from, to, id;
	Edge(int from, int to, int id) : from(from), to(to), id(id) {}
	Edge() {}
};

Edge cont[MAX_N];
vector<Edge> G[MAX_N];

int ppar[MAX_N], pdep[MAX_N];
int gr[MAX_N];
bool vis[MAX_N];
bool used[MAX_M];
bool loop[MAX_N];

void makeTree(int v)
{
	vis[v] = true;
	
	for (int i = 0; i < G[v].size(); i++){
		if (!vis[G[v][i].to]){
			used[G[v][i].id] = true;
			ppar[G[v][i].to] = v;
			pdep[G[v][i].to] = pdep[v] + 1;
			makeTree(G[v][i].to);
		}
	}
}

int depth[MAX_N];
int par[20][MAX_N];
int num[20][MAX_N];

vector<int> getCycle(int a, int b)
{
	vector<int> ret;
	if (pdep[a] < pdep[b]) swap(a, b);
	while (pdep[a] > pdep[b]){
		ret.push_back(a);
		a = ppar[a];
	}
	
	while (a != b){
		ret.push_back(a), ret.push_back(b);
		a = ppar[a], b = ppar[b];
	}
	ret.push_back(a);
	
	return (ret);
}

void dfs(int v)
{
	vis[v] = true;
	
	for (int i = 0; i < G[v].size(); i++){
		if (!vis[G[v][i].to]){
			depth[G[v][i].to] = depth[v] + 1;
			par[0][G[v][i].to] = v;
			num[0][G[v][i].to] = loop[G[v][i].to];
			dfs(G[v][i].to);
		}
	}
}

void initLCA()
{
	for (int i = 1; i < 20; i++){
		for (int j = 0; j < MAX_N; j++){
			if (~par[i - 1][j]){
				par[i][j] = par[i - 1][par[i - 1][j]];
				num[i][j] = num[i - 1][par[i - 1][j]] + num[i - 1][j];
			}
		}
	}
}

int countCycle(int a, int b)
{
	int ret = 0;
	if (depth[a] < depth[b]) swap(a, b);
	
	for (int i = 19; i >= 0; i--){
		if ((depth[a] - depth[b]) >> i & 1){
			ret += num[i][a];
			a = par[i][a];
		}
	}
	
	if (a == b) return (ret + num[0][a]);
	
	for (int i = 19; i >= 0; i--){
		if (par[i][a] != par[i][b]){
			ret += num[i][a];
			ret += num[i][b];
			a = par[i][a];
			b = par[i][b];
		}
	}
	ret += num[0][a] + num[0][b];
	a = par[0][a];
	
	return (ret + num[0][a]);
}

int main()
{
	int N, M;
	int two[MAX_N];
	
	two[0] = 1;
	for (int i = 1; i < MAX_N; i++){
		two[i] = (2 * two[i - 1]) % 1000000007;
	}
	
	scanf("%d %d", &N, &M);
	
	for (int i = 0; i < M; i++){
		scanf("%d %d", &cont[i].from, &cont[i].to);
		cont[i].id = i;
		G[--cont[i].from].push_back(Edge(cont[i].from, --cont[i].to, cont[i].id));
		G[cont[i].to].push_back(Edge(cont[i].to, cont[i].from, cont[i].id));
	}
	
	makeTree(0);
	
	memset(gr, -1, sizeof(gr));
	
	int idx = 0;
	for (int i = 0; i < M; i++){
		if (used[i] == false){
			vector<int> group = getCycle(cont[i].from, cont[i].to);
			for (int i = 0; i < group.size(); i++){
				gr[group[i]] = idx;
			}
			loop[idx++] = true;
		}
	}
	
	for (int i = 0; i < N; i++){
		if (gr[i] == -1){
			gr[i] = idx++;
		}
		G[i].clear();
	}
	
	for (int i = 0; i < M; i++){
		int a = gr[cont[i].from], b = gr[cont[i].to];
		G[a].push_back(Edge(a, b, i));
		G[b].push_back(Edge(b, a, i));
	}
	memset(par, -1, sizeof(par));
	memset(vis, 0, sizeof(vis));
	
	num[0][0] = loop[0];
	dfs(0);
	
	int Q;
	initLCA();
	scanf("%d", &Q);
	
	for (int i = 0; i < Q; i++){
		int a, b;
		scanf("%d %d", &a, &b);
		printf("%d\n", two[countCycle(gr[--a], gr[--b])]);
	}
	
	return (0);
}