[BZOJ4011] [HNOI2015]落忆枫音

[HNOI2015]落忆枫音

题目大意:给定一个拓扑图,然后加一条边,求加边后的以1点为根的外向生成树数目。

试题分析

题目真(和谐)长。。
首先肯定先考虑在一个拓扑图中如何求外向生成树个数:
对于每一个点\((2\leq i\leq n)\),很显然它一定要有一条入边,这样在只有1为入度为0的拓扑图中一定可以形成一棵树。
那么第一部分就是:$\prod_{i=2}^n degree_i \( 然后因为在加一条边后这个拓扑图可能变成有环的图,这样怎么办呢? 这道题很明显加一条边的目的之一就是让你来倒着想的。。。 也就是答案=总方案数(第一部分)-带环的方案数(第二部分) 既然带环,并且有边\)x\to y\(,那么一定有路径\)y\to x\(。 确定一个环以后剩下的点依旧可以随便连。 那么设点集\)S\(为\)y\to x\(的一条路径上的点集。 于是第二部分答案=\)\sum_S\prod {j\not \in S,1\leq j\leq n} degree_j\( 这一部分就可以\)dp\(出来,设\)f_i\(表示从\)y\to i\(的路径的上面的答案。 那么有转移:\)f_i=\frac{\sum f_j}{degree_i}$
然后最后减去\(f[X]\)就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
//#include<ctime>
//#include<cmath>
#include<queue>
 
using namespace std;
#define LL long long
 
inline LL read(){
    LL x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 300010;
const LL Mod = 1000000007;
 
LL N,M,X,Y; LL deg[MAXN+1]; LL cnt;
LL Node[MAXN<<1],Next[MAXN<<1],Root[MAXN+1];
inline void insert(LL u,LL v){
    Node[++cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt;
} LL sta[MAXN+1],top; LL f[MAXN+1];
LL inv[MAXN+1]; LL ans=1,in[MAXN+1];
vector<LL> vec[MAXN+1];
 
inline LL Top_sort(){
    for(LL i=2;i<=N;i++) if(!deg[i]) {
        puts("0"); exit(0);
    } else in[i]=deg[i]; queue<LL> que; que.push(1); bool flag=false;
    f[Y]=ans; 
    while(!que.empty()){
        LL k=que.front(); sta[++top]=k; que.pop(); 
        f[k]=f[k]*inv[deg[k]+(Y==k)]%Mod;
        for(LL x=Root[k];x;x=Next[x]){
            LL v=Node[x]; --in[v]; (f[v]+=f[k])%=Mod;
            if(!in[v]) que.push(v);
        }
    } 
    return f[X];
}
 
int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    N=read(),M=read(),X=read(),Y=read(); inv[1]=1;
    for(LL i=2;i<=max(N,M);i++) inv[i]=((Mod-(Mod/i))*inv[Mod%i])%Mod;
    for(LL i=1;i<=M;i++){
        LL u=read(),v=read();
        insert(u,v); ++deg[v]; vec[v].push_back(u);
    } for(LL i=2;i<=N;i++) ans=ans*(deg[i]+(i==Y))%Mod;
    if(Y==1) {printf("%lld\n",ans); return 0;}
    printf("%lld\n",(ans-Top_sort()+Mod)%Mod);
    return 0;
}
posted @ 2018-09-13 16:34  wxjor  阅读(185)  评论(0编辑  收藏  举报