[BZOJ4011]HNOI2015落叶枫音|拓扑序DP

窝不会做啊QAQ。。看了题解发现一个DAG每个点选一条入边就是一颗有根树(森林),所以DAG的生成有根树的数量就是所有入度不为0的点的入度积。那么在DAG上加了一条边之后还是这样算就可能会有一些不合法的情况(成环)了,假设加进来的是s->t的边,那么环中一定包含了一条t->s的路径,所以我们的任务就是找出原DAG上包含t->s的路径的生成有根树的数量然后减掉就行了。。如果有t->x这条边,那么考虑原DAG上包含x->s的路径的生成有根树的数量这样一个子问题,假设这个值是f[x]已知,那么转移到t的时候实际上就是x的入度只能选t->x这条边了,所有要用f[x]/x的入度,再累加到t上。这样就找出了拓扑图DP的转移,然后从t开始dp即可,初始f[s]=DAG入度乘积,求出f[t]之后最后答案就是DAG上加了一条边之后的入度乘积-f[t]/t的入度。

#include<iostream>
#include<cstdio>
#define ll long long
#define N 100005
#define M 200005
#define p 1000000007
using namespace std;
struct edge{
    int e,next;
} ed[M];
int n,m,X,y,i,s,e,ne=0,a[N],in[N],u[N];
ll ans,f[N];
ll pow(ll a,int q)
{
    if (q==0) return 1ll;
    ll b=pow(a,q/2);
    b=(b*b)%p;
    if (q%2) b=(b*a)%p;
    return b;
}
void add(int s,int e)
{
    ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void dp(int x)
{
    u[x]=1;
    if (x==X)f[x]=ans;else f[x]=0;
    for (int j=a[x];j;j=ed[j].next)
    {
        if (!u[ed[j].e]) dp(ed[j].e);
        f[x]=(f[x]+(f[ed[j].e]*pow((ll)in[ed[j].e],p-2))%p)%p;
    }
}
int main()
{
//  freopen("hn.in","r",stdin);
    scanf("%d%d%d%d",&n,&m,&X,&y);
    for (i=1;i<=n;i++) a[i]=in[i]=u[i]=0;
    for (i=1;i<=m;i++)
    {
        scanf("%d%d",&s,&e);
        add(s,e);
        in[e]++;
    }
    ans=1;
    if (X==y||y==1)
    {
        for (i=2;i<=n;i++) ans=(ans*in[i])%p;
        printf("%lld\n",ans);
        return 0;
    }
    in[y]++;
    for (i=2;i<=n;i++) ans=(ans*in[i])%p;
    dp(y);
    ans=(ans-(f[y]*pow((ll)in[y],p-2))%p+p)%p;
    printf("%lld\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值