51nod 1806 wangyurzee的树[purfer][容斥]

来源:互联网 发布:2016年网络诈骗追回率 编辑:程序博客网 时间:2024/05/16 18:36

可以转化为算有限制的purfer序列的方案数
满足m个条件的序列方案数可以直接算,详见BZOJ1005
然后知道n 个点的生成树的数量为 n^(n−2)
再分别求满足1,2,…,m个条件的方案数
容斥一下
这位大佬说得比较详细

#include<cstdio>#define LL long long#define N 1000000#define P 1000000007using namespace std;int n,m,a[N+5],b[N+5],mark[N+5];LL fac[N+5],inv[N+5];LL ksm(LL n,LL k){    LL tmp=1;    while(k)    {        if(k&1) tmp=tmp*n%P;        k>>=1;n=n*n%P;    }    return tmp;}void init(){    int i;    for(i=1,fac[0]=1;i<=n;++i) fac[i]=fac[i-1]*i%P;    for(i=2,inv[1]=1;i<=n;++i) inv[i]=(P-P/i)*inv[P%i]%P;    for(i=1,inv[0]=1;i<=n;++i) inv[i]=inv[i-1]*inv[i]%P;}int main(){    scanf("%d %d",&n,&m);    if(n==1) {puts("1");return 0;}    init();    int i;    for(i=1;i<=m;++i) scanf("%d %d",&a[i],&b[i]),--b[i];    LL ans=0;int state,all=1<<m;    for(state=0;state<all;++state)    {        for(i=1;i<=m;++i) mark[i]=0;        int cnt=0,num=0,tag=1,tmp=fac[n-2];        for(i=1;i<=m;++i)            if((state>>(i-1))&1)            {                if(mark[a[i]]){tag=0;break;}                else mark[a[i]]=1;                ++cnt;                num+=b[i];                tmp=1ll*tmp*inv[b[i]]%P;            }        if(!tag||num>n-2) continue;        tmp=1ll*tmp*inv[n-2-num]%P*ksm(n-cnt,n-2-num)%P;        if(cnt&1) ans=(ans-tmp+P)%P;        else ans=(ans+tmp)%P;    }    printf("%lld\n",ans);}