題目鏈接:http://noi.ac/problem/694
前置知識:一個聯通圖沒有有偶環=它是環套樹或者樹=仙人掌=沒有2個環共用相鄰的邊(over)
爲啥呢:因爲若是2個環有相鄰的邊,那麼就存在一個大環的邊數=奇+奇-2*重複的邊,顯然這是一個偶數(over)
環套樹的一些性質:m=n(自己想去)所以對於一個奇環森林就可以做到每個點配對一個邊。
好,現在考慮題目問的東西。求x,y之間的簡單路徑。而對於x,y來說,只要經過一個環,他們之間的簡單路徑總是就*2(自己想)
而在樹上的時候顯然2個點之間的最短路徑只有一條。
所以我們要求的東西就是這2個點之間有多少環。
這裏介紹一個神奇的算法(只有在環套樹的時候能用啊)
我們要構建一個方圓樹,圓點就是正常的點。方點是我們把一個環上所有的點都連在一個新建的點上。所以我們要找到就是方點的個數。(其實這是一個類似縮點的過程,noip2018Day2 T1旅行 也可以這樣寫,它是一個簡化的環套樹,不過沒必要。。。)
無向圖縮點我也懶得講了。。。自己想去。。。(參見tarjan算法)
現在找環=找方點。考慮lca,用w數組存一下每個點到根的方點的個數。再用lca回答一下詢問,做一下2的次冪,over。
#include <bits/stdc++.h>
using namespace std;
/*
這裏是一個環套樹
*/
const int maxn=200005;
const int mo=19491001;
int n,m,Q,head[maxn],nxt[maxn],v[maxn],w[maxn];//表示根到他有幾個方點
int dfn[maxn],low[maxn],pa[maxn],vis[maxn],d[maxn],f[maxn][31];
vector<int> q[maxn];
int indexs=0;int N=0;
void add_edge(int x,int y)
{
N++;
v[N]=y;nxt[N]=head[x];head[x]=N;
}
void tarjan(int x)
{
dfn[x]=low[x]=++indexs;
for(int i=head[x];i!=-1;i=nxt[i])
{
if(v[i]==pa[x]) continue;
if(!dfn[v[i]])
{
pa[v[i]]=x;
tarjan(v[i]);
if(low[v[i]]>dfn[x]) q[x].push_back(v[i]);
//v[i]到不了x的父親(沒有環) 把樹邊相連
low[x]=min(low[x],low[v[i]]);
}
else low[x]=min(low[x],dfn[v[i]]);
}
for(int i=head[x];i!=-1;i=nxt[i])
{
int z=x;
if(v[i]==pa[x]) continue;
if(dfn[v[i]]<dfn[x])//v[i]是x的祖先
{
w[++n]=1;
while(z!=v[i])
{
q[n].push_back(z);z=pa[z]; //把從x到v[i]中所有的點都搞到新建的方點裏面
//相當於一個環
}
q[n].push_back(v[i]);
}
}
//啥意思大概是所點啥的吧(搞成圓方樹?)
}
void dfs(int x,int fa)
{
w[x]+=w[fa];dfn[x]=++indexs;f[x][0]=fa;
for(int i=1;i<18;i++) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i!=-1;i=nxt[i]) if(v[i]!=fa) dfs(v[i],x);
}
inline int lca(int x,int y) {
if(x==y) return x;
if(dfn[x]<dfn[y]) swap(x,y);
for(int i=17;~i;i--)
if(f[x][i]&&dfn[f[x][i]]>dfn[y]) x=f[x][i];
return f[x][0];
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
add_edge(x,y);add_edge(y,x);
}
tarjan(1);
memset(head,-1,sizeof(head));N=0;
for(int i=1;i<=n;i++)
for(int j=0;j<q[i].size();j++)
if(vis[q[i][j]]!=i)
{
vis[q[i][j]]=i;
add_edge(q[i][j],i);add_edge(i,q[i][j]);
}
/*
1-n的q中存的應該是能幫助i走到祖先的點q[i][j]
這個地方已經構造成一棵樹了 (圓方樹)
i與q[i][j]相連(其實上面已經做過了這裏只是判重而已)
*/
dfs(1,0);
for(int i=d[0]=1;i<=n;i++) d[i]=2*d[i-1]%mo;
while(Q--) {
int x,y;scanf("%d%d",&x,&y);int z=lca(x,y);
printf("%d\n",d[w[x]+w[y]-w[z]-w[f[z][0]]]);
}
return 0;
}