[HNOI2015]落忆枫音

Description:

给你一个DAG,现在要在DAG上加一条边,问加之后可以生成多少个以1为根的树形图

Hint:

\(n \le 10^5\)

Solution:

考虑不加那条边,树形图个数就是 \(\prod in[i]\) (组合计数)

现在加了一条边,可能会形成环

考虑对于每个环,不合法的方案是 \(\frac{\prod in[i]}{\prod_{a_i \in Cir} in[a_i] }\)

现在我们只要统计出这些不合法的方案,拿总答案减去就是了

DAG上直接记搜即可

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=1e5+5,mod=1e9+7;
int n,m,cnt,x,y,tx,ty,ans=1,sum=1,g[mxn],in[mxn],hd[mxn],vis[mxn];

inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}

struct ed {
    int to,nxt;
}t[mxn<<1];

inline void add(int u,int v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

int qpow(int a,int b) {
    int res=1,bs=a;
    while(b) {
        if(b&1) res=1ll*res*bs%mod;
        bs=1ll*bs*bs%mod;
        b>>=1;
    }
    return res;
}

void dfs(int u) {
    if(vis[u]) return ; vis[u]=1;
    if(u==ty) {
        g[u]=1ll*sum*qpow(in[u],mod-2)%mod;
        return ;
    }
    for(int i=hd[u];i;i=t[i].nxt) {
        int v=t[i].to;
        dfs(v); g[u]=(g[u]+g[v])%mod;
    }
    g[u]=1ll*g[u]*qpow(in[u],mod-2)%mod;
}

int main()
{
    n=read(); m=read(); tx=read(); ty=read();
    for(int i=1;i<=m;++i) 
        x=read(),y=read(),add(y,x),++in[y];
    ++in[1];
    for(int i=1;i<=n;++i) {
        if(i==ty) ans=1ll*ans*(in[i]+1)%mod;
        else ans=1ll*ans*in[i]%mod;
        sum=1ll*sum*in[i]%mod;
    }
    dfs(tx); printf("%d",(ans-g[tx]+mod)%mod);
    return 0;
}

posted @ 2019-03-29 17:16  cloud_9  阅读(93)  评论(0编辑  收藏  举报