【BZOJ4011】【HNOI2015】落忆枫音 拓扑图DP,

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45362029");
}

题解:

如果没有后加的边,那么 ans=ni=2di ,可以回忆构建树形数据的普遍方法——点 i 连一条 [1,i-1] 的边即可。
然后后加边了以后,有且仅有一些方案会形成环是错误方案。
拓扑图DP就好啦~, f(i) 表示从 y i 时的方案。
发现对于一条 yi 的路径,再加上一条 iy 的路径就会形成环,然后减少的方案数为 (ni=2di,i) ,表示这条路径上的点已经选定,其它的随便选都是错的。

f(y)=initansdy
f(i)=f(j),jidi

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define M 201000
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;
struct Eli
{
    int v,next;
}e[M];
int head[N],cnt,d[N],D[N];
inline void add(int u,int v)
{
    d[v]++;
    e[++cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
long long inv[M];
void getinv()
{
    inv[1]=1;
    for(int i=2;i<M;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
int n,m,x,y;
int stk[N],top;
long long ans=1;
long long f[N];
int main()
{
    int i,j,k;
    int a,b,v;

    scanf("%d%d%d%d",&n,&m,&x,&y);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    for(i=1;i<=n;i++)D[i]=d[i];
    for(i=2;i<=n;i++)ans=ans*d[i]%mod;
    if(y==1||x==y)
    {
        cout<<ans<<endl;
        return 0;
    }
    getinv();
    ans=ans*inv[d[y]]%mod*(++d[y])%mod;
    f[y]=ans;

    for(i=1;i<=n;i++)if(!D[i])stk[++top]=i;
    while(top)
    {
        k=stk[top--];
        f[k]=(f[k]*inv[d[k]])%mod;
        for(i=head[k];i;i=e[i].next)
        {
            v=e[i].v;
            f[v]=(f[v]+f[k])%mod;
            D[v]--;
            if(!D[v])stk[++top]=v;
        }
    }
    cout<<(ans+mod-f[x])%mod<<endl;

    return 0;
}

你可能感兴趣的:(动态规划,拓扑图,HNOI2015,BZOJ4011,落忆枫音)