problem
给定一个 n n n 个点 m m m 条边的 DAG,保证 1 1 1 的入度是 0 0 0。
现在要加上一条 S → T S \rightarrow T S→T 的边( S S S 和 T T T 是给定的点),请求出加上这条边之后以 1 1 1 为根的树形图数量。
答案对 1 0 9 + 7 10^9+7 109+7 取模。
数据范围: 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, n − 1 ≤ m ≤ min ( 2 × 1 0 5 , n ( n − 1 ) 2 ) n - 1 \leq m \leq \min(2\times 10^5, \frac {n(n -1)} {2}) n−1≤m≤min(2×105,2n(n−1))。
solution
先考虑不加那条边,即一个 DAG 的答案是什么。
设点 x x x 的入度是 i n x in_x inx,那么答案是 ∏ i = 2 n i n i \prod_{i=2}^nin_i ∏i=2nini,相当于就是给每个点选个父亲。
现在加了一条边,可能会产生 1 1 1 个或多个环,有环的方案肯定不合法,用上面的算法会多算一些东西,考虑如何减掉。
对于一个环(可能有多个),假设环的大小为 k k k,环上的点为 a 1 , a 2 , ⋯ , a k a_1,a_2,\cdots,a_k a1,a2,⋯,ak,这个环多算的答案为:
∏ i = 2 n i n i ∏ i = 1 k i n a k \frac{\prod_{i=2}^nin_i}{\prod_{i=1}^kin_{a_k}} ∏i=1kinak∏i=2nini
意思就是,强制把这个环选出来,其它点的父亲依旧任意选。
由于可能出现多个环,考虑 d p dp dp 求答案。设 f i f_i fi 表示 d p dp dp 到 i i i 时的答案,那么:
f i = ∑ j → i f j i n i f_i=\frac{\sum_{j\rightarrow i}f_j}{in_i} fi=ini∑j→ifj
时间复杂度 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,P=1e9+7;
int n,m,t,S,T;
int f[N],in[N],deg[N],first[N],v[N],nxt[N];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y%P;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
void edge(int x,int y){
nxt[++t]=first[x],first[x]=t,v[t]=y;
}
void Topsort(){
queue<int>Q;
for(int i=1;i<=n;++i)
if(!in[i]) Q.push(i);
while(!Q.empty()){
int x=Q.front();Q.pop();
f[x]=mul(f[x],power(deg[x],P-2));
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(!(--in[to])) Q.push(to);
f[to]=add(f[to],f[x]);
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&S,&T);
deg[T]++;
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
edge(x,y),in[y]++,deg[y]++;
}
int ans=1;
for(int i=2;i<=n;++i) ans=mul(ans,deg[i]);
if(T==1) return printf("%d\n",ans),0;
f[T]=ans,Topsort(),printf("%d\n",dec(ans,f[S]));
return 0;
}