1. 程式人生 > >[bzoj4011] [HNOI2015]落憶楓音

[bzoj4011] [HNOI2015]落憶楓音

題目描述

不妨假設楓葉上有 n個穴位,穴位的編號為 1 ~ n。有若干條有向的脈絡連線著這些穴位。穴位和脈絡組成一個有向無環圖——稱之為脈絡圖(例如圖 1),穴位的編號使得穴位 1 沒有從其他穴位連向它的脈絡,即穴位 1 只有連出去的脈絡;由上面的故事可知,這個有向無環圖存在一個樹形子圖,它是以穴位 1為根的包含全部n個穴位的一棵樹——稱之為脈絡樹(例如圖 2和圖 3給出的樹都是圖1給出的脈絡圖的子圖);值得注意的是,脈絡圖中的脈絡樹方案可能有多種可能性,例如圖2和圖 3就是圖 1給出的脈絡圖的兩個脈絡樹方案。

img

脈絡樹的形式化定義為:以穴位 r 為根的脈絡樹由楓葉上全部 n個穴位以及 n- 1 條脈絡組成,脈絡樹裡沒有環,亦不存在從一個穴位連向自身的脈絡,且對於楓葉上的每個穴位 s,都存在一條唯一的包含於脈絡樹內的脈絡路徑,使得從穴位r 出發沿著這條路徑可以到達穴位 s。 現在向脈絡圖新增一條與已有脈絡不同的脈絡(注意:連線 2個穴位但方向不同的脈絡是不同的脈絡,例如從穴位3到4的脈絡與從4到3的脈絡是不同的脈絡,因此,圖 1 中不能新增從 3 到 4 的脈絡,但可新增從 4 到 3 的脈絡),這條新脈絡可以是從一個穴位連向自身的(例如,圖 1 中可新增從 4 到 4 的脈絡)。原脈絡圖新增這條新脈絡後得到的新脈絡圖可能會出現脈絡構成的環。 請你求出添加了這一條脈絡之後的新脈絡圖的以穴位 1 為根的脈絡樹方案數。

由於方案可能有太多太多,請輸出方案數對 1,000,000,007 取模得到的結果。

Input

輸入檔案的第一行包含四個整數 n、m、x和y,依次代表楓葉上的穴位數、脈絡數,以及要新增的脈絡是從穴位 x連向穴位y的。 接下來 m行,每行兩個整數,由空格隔開,代表一條脈絡。第 i 行的兩個整數為ui和vi,代表第 i 條脈絡是從穴位 ui連向穴位vi的。

Output

輸出一行,為添加了從穴位 x連向穴位 y的脈絡後,楓葉上以穴位 1 為根的脈絡樹的方案數對 1,000,000,007取模得到的結果。

Sample Input

4 4 4 3
1 2
1 3
2 4
3 2

Sample Output

3

Solution

對於一個\(DAG\),答案顯然是:
\[ \prod_{i=2}^{n}deg_i \]
其中\(deg_i\)表示\(i\)的入度,每個點選一個點做父親,一定可以構成一棵樹。

加入了一條邊,就多出了一些環,先不管這些環,還是按上面把答案算出來,

顯然,多算了一些環的情況,可以發現,構造出來的圖最多出現一個環。

所以暴力的話就直接列舉每一個環,答案就是:
\[ \prod_{i=2}^ndeg_i-\sum\prod_{i\notin circle} deg_i \]
後面的連乘也就是:
\[ \cfrac{\prod_{i=2}^{n}deg_i}{\prod_{i\in circle}deg_i} \]


考慮到每個環一定是由\((x,y)\)\(y \to x\)的一條路徑構成的,所以設\(g(u)\)\(u\)\(x\)的路徑上上面式子的貢獻,轉移就是:
\[ g(u)=\cfrac{\sum_{(u,v)\in E}g(v)}{deg_u} \]
其中\(E\)為邊集。

然後答案就是:
\[ \prod_{i=2}^ndeg_i-g(y) \]

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+10;
const int mod = 1e9+7;

int inv[maxn],n,m,g[maxn],head[maxn],tot,x,y,deg[maxn],ans=1,vis[maxn];
struct edge{int to,nxt;}e[maxn<<1];

void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}

void dfs(int u) {
    if(vis[u]) return ;vis[u]=1;
    if(u==x) return g[u]=1ll*ans*inv[deg[u]]%mod,void();
    for(int i=head[u];i;i=e[i].nxt)
        dfs(e[i].to),g[u]=(g[u]+g[e[i].to])%mod;
    g[u]=1ll*g[u]*inv[deg[u]]%mod;
}

int main() {
    inv[1]=1;
    for(int i=2;i<maxn;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    read(n),read(m),read(x),read(y);deg[1]=1;
    for(int i=1,u,v;i<=m;i++) read(u),read(v),ins(u,v),deg[v]++;
    for(int i=2;i<=n;i++) ans=1ll*ans*deg[i]%mod;
    dfs(y);int delta=g[y];
    ans=1ll*ans*inv[deg[y]]%mod*(deg[y]+1)%mod;
    write((ans-delta+mod)%mod);
    return 0;
}