[BZOJ4011][HNOI2015]落忆枫音-[dp乱搞+拓扑排序]

Description

传送门

Solution

假如我们的图为DAG图,总方案数ans为每个点的入度In相乘(不算1号点)。(等同于在每个点的入边选一条边,最后一定构成一棵树)。

然而如果加了边x->y后图中带了环,则ans个方案中不合法的方案一定是选择了原DAG图中y->x的路径后又选了额外加的边x->y。

假如说我们找到了某条y->x的路径,则选了这条路径的不合法方案数就为除了该路径上的其他点入度相乘。

考虑在原图上dp。假如原图上存在了一条u->v的路径,dp[u]+=dp[v]*inv(In[v])。边界dp[t]=ans*inv(In[t])。

为了保证当我们处理到v的时候所有与指向u的边已经被处理完毕,dp得按照拓扑序进行。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll ksm(ll x,int k)
{
    ll re=1;
    while (k)
    {
        if (k&1) re=re*x%mod;
        k>>=1;
        x=x*x%mod;
    }
    return re;
}

int n,m,s,t,x,y,In[100010];
struct node{int y,nxt;
}g[200010];int h[100010],tot=0;
ll ans,inv[100010];
ll dp[100010];
queue<int>q;
void bfs()
{
    dp[t]=ans*inv[t];
    for (int i=1;i<=n;i++) if (!In[i]) q.push(i);
    while (!q.empty())
    {
        x=q.front();
        q.pop();
        for (int i=h[x];i;i=g[i].nxt)
        {
            In[g[i].y]--;
            if (!In[g[i].y]) q.push(g[i].y);
            dp[g[i].y]=(dp[g[i].y]+dp[x]*inv[g[i].y]%mod)%mod;
        }
    }
    ans-=dp[s];ans%=mod;if (ans<0) ans+=mod;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        g[i]=node{y,h[x]};h[x]=i;
        In[y]++;
    }
    In[t]++;ans=1;
    for (int i=2;i<=n;i++) inv[i]=ksm(In[i],mod-2),ans=ans*In[i]%mod;
    if (t==1) {cout<<ans;return 0;}
    In[t]--;
    bfs();
    cout<<ans;
}
原文地址:https://www.cnblogs.com/coco-night/p/9526097.html