題目鏈接: http://codeforces.com/problemset/problem/231/E
題意:
你現在有一個 個點的仙人掌圖(每個邊最多隻屬於一個簡單環),定義一條簡單路徑爲一條邊最多隻被走過一次的路徑。你現在有 個詢問,每次詢問從 有多少條簡單路徑,答案 。
做法:
如果你得到的是一棵樹,那麼很明顯查詢的結果一定是 但是仙人掌圖中的環會帶來額外的答案,很容易得出如果從 到 經過了 個環,那麼答案就是 。
這裏的經過不一定進入到環的內部,只是經過在環上的點也是算在答案中的(經過的時候繞環一圈也是符合簡單路徑的),這就帶來了一些麻煩。
這個時候就想到了圓方樹,我們先將整個圖變成一棵樹,在碰到入環的點的時候把這個值賦值爲 ,然後用一次 算出到每個點的距離,用 可以得到一個除了沒加 以外的總值。
最後我們只要判斷這個 是否在環上即可。
爲什麼呢,首先上面的加減是必要的。但是我們的 如果在環上, 那麼這個環的影響會因爲減去了兩倍的 而消失,所以這個環我們還需要額外加上。
如果還不理解的話畫個圖感受一下就好啦。
代碼
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int maxn=100005;
const int maxm=300005;
const ll mod=1e9+7;
int low[maxn],dfn[maxn],dep[maxn],cou,is[maxn];
int n,m,q,fa[maxn][25],onC[maxn];
int S[maxn],top,dis[maxn],totp;
ll twoq[maxn];
struct Graph{
int cnt,nex[maxm],to[maxm];
int head[maxn];
Graph(){memset(head,-1,sizeof(head)); cnt=0;}
void add(int u,int v){
to[cnt]=v;nex[cnt]=head[u];
head[u]=cnt++;
}
}G,Cactu;
void dfs(int u,int f){
for(int i=Cactu.head[u];~i;i=Cactu.nex[i]){
int v=Cactu.to[i];
if(v==f) continue;
fa[v][0]=u,dep[v]=dep[u]+1,dis[v]=dis[u]+is[v];
dfs(v,u);
}
}
void deal(int u,int v){
++totp; is[u]=1;
onC[u]=onC[v]=onC[totp]=1;
while(1){
int p=S[top--];
Cactu.add(totp,p);
onC[p]=1;
if(p==v) break;
}
Cactu.add(u,totp);
}
void tarjan(int u,int f){
dfn[u]=low[u]=++cou;
S[++top]=u;
for(int i=G.head[u];~i;i=G.nex[i]){
int v=G.to[i];
if(v==f) continue;
if(!dfn[v]){
tarjan(v,u);
//這條邊爲非環邊 直接建圖
if(low[v]>dfn[u]) top--,Cactu.add(u,v);
//如果u和v是恰好連接一個環的邊
else if (low[v]==dfn[u]) deal(u,v);
low[u]=min(low[u],low[v]);
}
//這條邊連回更小的位置
else low[u]=min(low[u],dfn[v]);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=22;i>=0;i--)
if(dep[x]-(1<<i)>=dep[y])
x=fa[x][i];
if(x==y) return x;
for(int i=22;i>=0;i--)
if(fa[x][i]!=fa[y][i]&&fa[x][i]!=-1){
x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
}
int main(){
memset(fa,-1,sizeof(fa));
twoq[0]=1;
for(int i=1;i<=100000;i++) twoq[i]=twoq[i-1]*2ll%mod;
scanf("%d%d",&n,&m); totp=n;
rep(i,1,m){
int x,y,z;scanf("%d%d",&x,&y);
G.add(x,y); G.add(y,x);
}
tarjan(1,-1);
dis[1]=is[1];
dfs(1,-1);
for(int j=1;(1<<j)<=totp;j++){
for(int i=1;i<=totp;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
scanf("%d",&q);
while(q--){
int u,v; scanf("%d%d",&u,&v);
int lca=LCA(u,v);
int ans=dis[u]+dis[v]-2*dis[lca];
if(onC[lca]) ans++;
printf("%lld\n",twoq[ans]);
}
return 0;
}