BZOJ4011: [HNOI2015]落憶楓音(dp 乘法原理)

自為風月馬前卒發表於2018-11-29

題意

題目連結

Sol

非常妙的一道題

(inder[i])表示(i)號節點的度數

首先如果是個DAG的話,可以考慮在每個點的入邊中選一條邊作為樹形圖上的邊,這樣(ans = prod_{i > 1} inder[i])

如果加入一條邊的話,算答案的時候可能會把一些環的貢獻也算進去(比如樣例中(2 – 4 – 3))這個環

考慮減去環上的貢獻,注意形成的環不止一個,準確的來說,如果加入了(x -> y)這條邊,那麼在原圖中所有(y -> x)的路徑都應該計算貢獻

其中一條路徑的貢獻為(frac{ans}{S in (y -> x) inder[S]})

dp一遍求出所有貢獻即可

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10, mod = 1e9 + 7;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < `0` || c > `9`) {if(c == `-`) f = -1; c = getchar();}
    while(c >= `0` && c <= `9`) x = x * 10 + c - `0`, c = getchar();
    return x * f;
}
int N, M, X, Y, inder[MAXN], inv[MAXN], t[MAXN], f[MAXN];
vector<int> v[MAXN];
void add(int &x, int y) {
    if(x + y < 0) x = x + y + mod;
    else x = (x + y >= mod ? x + y - mod : x + y);
}
int mul(int x, int y) {
    return 1ll * x * y % mod;
}
void Topsort() {
    queue<int> q;
    for(int i = 1; i <= N; i++) if(!inder[i]) q.push(i); 
    while(!q.empty()) {
        int p = q.front(); q.pop(); f[p] = mul(f[p], inv[t[p]]); 
        for(int i = 0; i < v[p].size(); i++) {
            int to = v[p][i];
            add(f[to], f[p]); 
            if(!(--inder[to])) q.push(to);
        }
    }
}
int main() {
    N = read(); M = read(); X = read(); Y = read();
    inv[1] = 1; for(int i = 2; i <= M + 1; i++) inv[i] = mul((mod - mod / i), inv[mod % i]); 
    for(int i = 1; i <= M; i++) {
        int x = read(), y = read();
        v[x].push_back(y); inder[y]++;
    }
    int ans = 1; inder[Y]++; 
    for(int i = 2; i <= N; i++) ans = mul(ans, inder[i]); 
    if(Y == 1) {cout << ans; return 0;}
    memcpy(t, inder, sizeof(inder));
    inder[Y]--;
    f[Y] = ans; Topsort();
    cout << (ans - f[X] + mod) % mod;
    return 0;
}

相關文章