BZOJ 4011 HNOI2015 落忆枫音

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4011

 

题目很长,写得也很有诗意与浪漫色彩,让我们不禁感叹出题人是一个多么英俊潇洒的人。

 

所以题目大意就是:

  给一个有向无环图,问加上一条我给定的边后,有多少个以1为根的树形图?n<=1e5,m<=2*1e5

原图无重边,加上的边可以为自环。

  

首先先来解决一个问题:

  一个有向无环图的树形图有多少个呢? 

相当于除了根节点以外,每个点随意取一个入度,为什么就一棵树呢?每个点选一个父亲,并且保证没有环,所以是一棵树。

好的,对于有向无环图就一定是这样的,那么若加入一条边<x,y>,

那么就可能再选择的过程中选出一个环[就是祖先的父亲是自己这种情况]。那么我们就需要删去这种情况。

首先要出现环,则必定包括了<x,y>这条边,剩下的部分是原图中y->x的一条路径,要求这条路径上的点必须选择一条路径使得构成一个环。而其它的点可以随意选。

设F(i)表示y->i上的点所成路径必须选择一条能构成环路径的方案数。

有初始值:

      

递推式:

   

相当于j可以选择延续所有从y走来的i的路径,但是要除以j原本可以选择的路径总数。

最后答案=所有方案[入度乘积] - F(x)。

 

听说ZZD去年就想出来了这题,不过因为不会打逆元而失之交臂[不然就是省队了?%%%]

为什么会有逆元?...额我好像忘记说方案数要取模了...

那么这题中为了加速运算,就用了一个O(n)递推求1...n的所有逆元的方法,我也算是普及一下吧?...

画图的时候没太注意大小写,不要太在意...

那么i的逆元可以由p mod i的逆元得知了,这显然是一个已知的数,所以每次递推都是O(1)的,边界:1的逆元=1。

 

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

inline int in(){
    int x=0;char ch=getchar();
    while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}

const int maxn=100010;
const int mod=1e9+7;
typedef long long ll;

struct Node{
    int data,next;
}node[maxn<<1];

#define now node[point].data
#define then node[point].next

int n,m,ans,cnt,x,y;
int head[maxn],d[maxn],ld[maxn];
int f[maxn],stack[maxn],top;
int inv[maxn<<1];

void init(){
    inv[1]=1;
    for(int i=2;i<=m;i++)
        inv[i]=(mod-(ll)inv[mod%i]*(mod/i)%mod)%mod;
}

void add(int u,int v){
    node[cnt].data=v;node[cnt].next=head[u];head[u]=cnt++;d[v]++;
}

int power(int a,int k){
    int ans=1;
    for(;k;k>>=1){
        if(k&1) ans=(ll)ans*a%mod;
        a=(ll)a*a%mod;
    }
    return ans;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("maple.in","r",stdin);
    freopen("maple.out","w",stdout);
#endif

    int u,v;

    n=in(),m=in();
    x=in(),y=in();
    init();
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=m;i++)
        u=in(),v=in(),add(u,v);
    d[y]++;
    ans=1;
    for(int i=2;i<=n;i++) ans=(ll)ans*d[i]%mod; 
    if(y==1){ printf("%d",ans);return 0;}
    else f[y]=(ll)ans*inv[d[y]]%mod;
    
    d[y]--;
    memcpy(ld,d,sizeof(d));
    for(int i=1;i<=n;i++) if(!d[i]) stack[++top]=i;
    
    while(top){
        u=stack[top--];
        for(int point=head[u];point!=-1;point=then){
            if(--d[now]==0)
                stack[++top]=now;
            f[now]=(f[now]+(ll)f[u]*inv[ld[now]]%mod)%mod;
        }
    }
    
    ans=(ans-f[x]+mod)%mod;
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2016-03-04 10:48  诚叙  阅读(268)  评论(0编辑  收藏  举报