HNOI2015 落忆枫音

Description


 
 
Input
Output
 
Sample Input
输入1:
4 4 4 3
1 2
1 3
2 4
3 2
输入2:
10 16 7 2
1 2
1 8
2 3
2 4
3 5
3 6
4 5
4 6
5 7
6 7
7 10
8 4
8 6
8 9
9 6
9 10
Sample Output
输出1:
3
 
       
      

输出2:
92
 
Data Constraint

 
首先有一个重要结论!
对于一个有向无环图(DAG),它的生成树个数就等于除根外所有点入度的乘积!
我考试的时候不知道这玩意然后直接GG了。。。
 
然后我们加入了一条边,可能会形成环,有环仍按以上方法计算会有不合法的情况,那么我们用总方案减去不合法的方案即是Ans
 
不合法方案的环中一定会包含我们新加入的边x->y
那么假设环中剩余的路径y->x,那么形成这样的环的方案数即为,总数/(在路径上的点的入度积),因为在路径上的点入度只能选路径上的边,其他的点仍可以随便选,
除法用逆元处理,那么我们设G[i]为所有y到i路径上的点的逆元的路的总和,按拓扑序dp一遍求出G[x]即可
 
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
typedef long long ll;

int mo=1000000007;
int du[100011],fc[100011],g[100011],next[200011],y[200011],que[200011];
int l,r,x,j,k,n,m,tx,ty,z,i,ans,tt,la;
int f[100011];

void star(int i,int j)
{
    tt++;
    next[tt]=g[i];
    g[i]=tt;
    y[tt]=j;
}

int mi(int x,int z)
{
    int l;
    l=1;
    while(z){
        if(z%2==1)l=(ll)l*x%mo;
        z/=2;
        x=(ll)x*x%mo;
    }
    return l;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&tx,&ty);
    for(i=1;i<=m;i++){
        scanf("%d%d",&x,&z);
        du[z]++;
        star(x,z);
    }
    if(tx==ty||ty==1){
        ans=1;
        for(i=2;i<=n;i++)ans=(ll)ans*du[i]%mo;
        printf("%d\n",ans);
    }
    else{
        ans=1;
        la=1;
        for(i=2;i<=n;i++){
            la=(ll)la*du[i]%mo;
            if(i==ty)ans=(ll)ans*(du[i]+1)%mo;
            else ans=(ll)ans*du[i]%mo;
            fc[i]=mi(du[i],mo-2);
        }
        l=r=1;
        que[l]=1;
        while(l<=r){
            x=que[l];
            j=g[x];
            while(j!=0){
                k=y[j];
                du[k]--;
                if(du[k]==0&&k!=ty){
                    r++;
                    que[r]=k;
                }
                j=next[j];
            }
            l++;
        }
        f[ty]=fc[ty];
        l=r=1;
        que[l]=ty;
        while(l<=r){
            x=que[l];
            j=g[x];
            while(j!=0){
                k=y[j];
                f[k]=((ll)f[k]+(ll)f[x]*fc[k]%mo)%mo;
                du[k]--;
                if(du[k]==0){
                    r++;
                    que[r]=k;
                }
                j=next[j];
            }
            l++;
        }
        la=(ll)la*f[tx]%mo;
        ans=ans-la;
        ans=(ans%mo+mo)%mo;
        printf("%d\n",ans);
    }
}

 

转载于:https://www.cnblogs.com/applejxt/p/4461566.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值