[luogu3244 HNOI2015] 落憶楓音(容斥原理+拓撲排序)
阿新 • • 發佈:2018-07-27
沒有 容斥 mes ont 一行 www 只需要 a* ()
傳送門
Description
給你一張?n?個點?m?條邊的DAG,1?號節點沒有入邊。再向這個DAG中加入邊?x→y?,求形成的新圖中以?1?為根的外向樹形圖數 模?10^9+7?。
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
HINT
對於所有測試數據,1 <= n <= 100000,n - 1 <= m <= min(200000, n(n -1) / 2),
1 <= x, y, ui, vi <= n。
Solution
直接處理外向樹形圖的數目比較困難,考慮容斥,用 每個點選一條入邊的方案數 減去 每個點選一條入邊形成不了外向樹形圖的方案數 得到答案。
每個點選一條入邊的方案數直接求
對於無法形成外向樹形圖的情況顯然是出現了一個環(除自環)而我們知道x和y顯然就在環中,那麽我們只需要從y到x跑一個拓撲排序+dp求出y到x的路徑數所占總路徑數的比例即可
Code
//By Menteur_Hxy #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define F(i,a,b) for(register int i=(a);i<=(b);i++) using namespace std; typedef long long LL; int read() { int x=0,f=1; char c=getchar(); while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();} while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar(); return x*f; } const int N=100010,MOD=1000000007; bool vis[N]; LL ans,du[N],f[N],de[N]; vector <int> V[N]; queue <int> Q; LL qpow(LL a,LL b) { LL t=1; while(b) { if(b&1) t=t*a%MOD; a=a*a%MOD; b>>=1; } return t; } void dfs(int x) { int siz=V[x].size(); F(i,0,siz-1) if(!vis[V[x][i]]) vis[V[x][i]]=1,dfs(V[x][i]); } int main() { int n=read(),m=read(),s=read(),t=read(),u,v; ans=du[1]=1; du[t]++; F(i,1,m) u=read(),v=read(),V[u].push_back(v),du[v]++; F(i,1,n) ans=ans*du[i]%MOD,du[i]=qpow(du[i],MOD-2); vis[t]=1; dfs(t); F(i,1,n) { int siz=V[i].size(); F(j,0,siz-1) if(vis[i]&&vis[v=V[i][j]]) de[v]++; } f[t]=du[t]; Q.push(t); while(!Q.empty()) { u=Q.front(); Q.pop(); int siz=V[u].size(); F(i,0,siz-1) if(vis[v=V[u][i]]) { f[v]=(f[v]+f[u]*du[v])%MOD; de[v]--; if(!de[v]) Q.push(v); } } printf("%lld",ans*(1-f[s]+MOD)%MOD); return 0; }
[luogu3244 HNOI2015] 落憶楓音(容斥原理+拓撲排序)