bzoj4011:[HNOI2015]落忆枫音
传送门
首先考虑不加边,那么就是一个有向无环图
答案的统计就是\(\sum_{i=2}^{n}in[i]\)(\(in[i]\)就是\(i\)号点的入度)
但是考虑加边之后会出现环,那么就会导致重复计数,需要将重复的部分去掉
重复的原因就是因为环边可能会走到重复的点
现在考虑新加的边\(x->y\)
我们可以发现,只有\(y->x\)的路径会被重复计算,计算出包含这条路径的树有多少种方案,减掉就好了
因为在DAG上,拓扑序dp
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e5+10,mod=1e9+7;bool vis[maxn];int f[maxn];queue<int>q;
int cnt,n,m,x,y,ans=1,in[maxn],pre[maxn*2],nxt[maxn*2],h[maxn],ny[maxn];
void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
int mi(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)ans=1ll*ans*a%mod;
b>>=1,a=1ll*a*a%mod;
}
return ans;
}
void dfs()
{
queue<int>q;
f[y]=1ll*ans*ny[y]%mod;
for(rg int i=1;i<=n;i++)if(!in[i])q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();
for(rg int i=h[x];i;i=nxt[i])
{
(f[pre[i]]+=(1ll*f[x]*ny[pre[i]])%mod)%=mod;
if(!(--in[pre[i]]))q.push(pre[i]);
}
}
ans=(ans-f[x]+mod)%mod;
}
int main()
{
read(n),read(m),read(x),read(y),in[y]++;
for(rg int i=1,x,y;i<=m;i++)read(x),read(y),in[y]++,add(x,y);
for(rg int i=2;i<=n;i++)ans=1ll*ans*in[i]%mod;
if(y==1){printf("%d\n",ans);return 0;}
for(rg int i=1;i<=n;i++)ny[i]=mi(in[i],mod-2);in[y]--;
dfs();printf("%d\n",ans);
}