[HNOI2015]落忆枫音
题目大意:给定一个拓扑图,然后加一条边,求加边后的以1点为根的外向生成树数目。
试题分析
题目真(和谐)长。。
首先肯定先考虑在一个拓扑图中如何求外向生成树个数:
对于每一个点((2leq ileq n)),很显然它一定要有一条入边,这样在只有1为入度为0的拓扑图中一定可以形成一棵树。
那么第一部分就是:$prod_{i=2}^n degree_i (
然后因为在加一条边后这个拓扑图可能变成有环的图,这样怎么办呢?
这道题很明显加一条边的目的之一就是让你来倒着想的。。。
也就是答案=总方案数(第一部分)-带环的方案数(第二部分)
既然带环,并且有边)x o y(,那么一定有路径)y o x(。
确定一个环以后剩下的点依旧可以随便连。
那么设点集)S(为)y o x(的一条路径上的点集。
于是第二部分答案=)sum_Sprod {j
ot in S,1leq jleq n} degree_j(
这一部分就可以)dp(出来,设)f_i(表示从)y o i(的路径的上面的答案。
那么有转移:)f_i=frac{sum{i o j} f_j}{degree_i}$
然后最后减去(f[X])就可以了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
//#include<ctime>
//#include<cmath>
#include<queue>
using namespace std;
#define LL long long
inline LL read(){
LL x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 300010;
const LL Mod = 1000000007;
LL N,M,X,Y; LL deg[MAXN+1]; LL cnt;
LL Node[MAXN<<1],Next[MAXN<<1],Root[MAXN+1];
inline void insert(LL u,LL v){
Node[++cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt;
} LL sta[MAXN+1],top; LL f[MAXN+1];
LL inv[MAXN+1]; LL ans=1,in[MAXN+1];
vector<LL> vec[MAXN+1];
inline LL Top_sort(){
for(LL i=2;i<=N;i++) if(!deg[i]) {
puts("0"); exit(0);
} else in[i]=deg[i]; queue<LL> que; que.push(1); bool flag=false;
f[Y]=ans;
while(!que.empty()){
LL k=que.front(); sta[++top]=k; que.pop();
f[k]=f[k]*inv[deg[k]+(Y==k)]%Mod;
for(LL x=Root[k];x;x=Next[x]){
LL v=Node[x]; --in[v]; (f[v]+=f[k])%=Mod;
if(!in[v]) que.push(v);
}
}
return f[X];
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read(),M=read(),X=read(),Y=read(); inv[1]=1;
for(LL i=2;i<=max(N,M);i++) inv[i]=((Mod-(Mod/i))*inv[Mod%i])%Mod;
for(LL i=1;i<=M;i++){
LL u=read(),v=read();
insert(u,v); ++deg[v]; vec[v].push_back(u);
} for(LL i=2;i<=N;i++) ans=ans*(deg[i]+(i==Y))%Mod;
if(Y==1) {printf("%lld
",ans); return 0;}
printf("%lld
",(ans-Top_sort()+Mod)%Mod);
return 0;
}