「HNOI 2015」落忆枫音

题目链接

戳我

\(Description\)

给一张\(n\)割点\(m\)条边的\(DAG\),保证点\(1\)不存在入边,现在需要在\(DAG\)中加入一条不在原图中的边\((x,y)\),求这个有向图以\(1\)为根的树形图个数对\(1e9+7\)去模的结果

\(n<=100000,m<=200000\)

\(Solution\)

我们首先来看看如果没有\((x,y)\)这一条边的话,在\(DAG\)上的方案数为多少?

\[ans=\prod_{i=1}^{n} vis[i] \]

\(vis\)为,点的入度

至于为什么?乘法原理就好了,很容易证明的.

现在来看看有了\((x,y)\)的限制如何处理?

因为加入了这条边以后可能会存在环,所以不能单单这么算了,我们还应减去不合法的情况.那么不合法情况怎么算呢?

我们还是令:

\[ans=\prod_{i=1}^{n} vis[i] \]

假设现在有一个环,中间的节点为:\(a_1,a_2...a_k\)

则不合法的情况为:

\[\frac{ans}{\prod_{i=1}^kvis[i]} \]

因为除了环的点的入度已经固定以外其他的可以随便选

所以我们可以用\(f[v]\) 记录从\(y\)\(v\frac{ans}{\prod_{i=1}^k vis[i]}\)的值

转移方程为(存在一条从\(u->v\)的路径):

\[f[u]=\sum f[v] \]

最后的答案就是:

\[\prod_{i=1}^{n} (i==y)?vis[i+1]:vis[i]-f[x] \]

注意一点要建反向边,因为要判断环.

\(Code\)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int read() {
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
struct node {
    int to,next;
}a[100010<<1];
int vis[100010],head[100010],cnt,n,m,x,y,u,v,ans=1,sum=1,bj[100010],f[100010];
void add(int x,int y){
    a[++cnt].to=y,a[cnt].next=head[x],head[x]=cnt;
}
int ksm(int a,int b){
    int ans=1;
    while(b){
	    if(b&1)
	        ans=ans*a%mod;
    	a=a*a%mod;
	    b>>=1;
    }
    return ans%mod;
}
int inv(int x){
    return ksm(x,mod-2)%mod;
}
void dfs(int x){
    if(bj[x])
	    return ;
    bj[x]=1;
    if(x==y){
	    f[x]=sum*inv(vis[x])%mod;
	    return ;
    }
    for(int i=head[x];i;i=a[i].next){
	    int v=a[i].to;
	    dfs(v);
	    f[x]=(f[x]+f[v])%mod;
    }
    f[x]=f[x]*inv(vis[x])%mod;
}
main(){
    n=read(),m=read(),x=read(),y=read();
    for(int i=1;i<=m;i++)
	    u=read(),v=read(),add(v,u),vis[v]++;
    vis[1]++;
    for(int i=1;i<=n;i++)
	    sum=sum*vis[i]%mod,(i==y)?ans=ans*(vis[i]+1)%mod:ans=ans*vis[i]%mod;
    dfs(x);
    printf("%lld ",(ans-f[x]+mod)%mod);
}

posted @ 2019-02-21 11:38  撤云  阅读(122)  评论(0编辑  收藏  举报
……