cf 231 E. Cactus 仙人掌圖圓方樹

題目鏈接: http://codeforces.com/problemset/problem/231/E

題意:

你現在有一個 1e51e5 個點的仙人掌圖(每個邊最多隻屬於一個簡單環),定義一條簡單路徑爲一條邊最多隻被走過一次的路徑。你現在有 1e51e5 個詢問,每次詢問從 a>ba->b 有多少條簡單路徑,答案 modmod 1e9+71e9+7

做法:

如果你得到的是一棵樹,那麼很明顯查詢的結果一定是 11 但是仙人掌圖中的環會帶來額外的答案,很容易得出如果從 xxyy 經過了 kk 個環,那麼答案就是 2kmod(1e9+7)2^k mod(1e9+7)

這裏的經過不一定進入到環的內部,只是經過在環上的點也是算在答案中的(經過的時候繞環一圈也是符合簡單路徑的),這就帶來了一些麻煩。

這個時候就想到了圓方樹,我們先將整個圖變成一棵樹,在碰到入環的點的時候把這個值賦值爲 11 ,然後用一次 dfsdfs 算出到每個點的距離,用 dis[x]+dis[y]2dis[lca=LCA(x,y)]dis[x]+dis[y]-2dis[lca=LCA(x,y)] 可以得到一個除了沒加 lcalca 以外的總值。

最後我們只要判斷這個 lcalca 是否在環上即可。

爲什麼呢,首先上面的加減是必要的。但是我們的 lcalca 如果在環上, 那麼這個環的影響會因爲減去了兩倍的 dis[lca]dis[lca] 而消失,所以這個環我們還需要額外加上。

如果還不理解的話畫個圖感受一下就好啦。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章