zoukankan      html  css  js  c++  java
  • [P3244][HNOI2015] 落忆枫音 (树上DAG+组合数)

    题意:给一个DAG,然后多加一条边,求在这个图中有多少种不同的生成树;

    解法:树上DAG+组合数;

    1.树上DAG:因为在DAG上多加了一条边,所以原图会出现环,那么多算的答案就是 rel = ans / ( 环上的点的入度的累乘 ),最后的答案就是 ans-rel(要注意,多加了一条边后,图上可能不止一个环,此时的 rel就是∑tot,tot = ans / ( 环上的点的入度的累乘 ));我们可以在计算这个多算出来的贡献时使用 DP,而在 DP时可以在原图上进行,因为我们的结束边界是多加的那一条边的 end;

    2.组合数:当图还是一个DAG时,由乘法原理可得 ans就是所有入度的乘积(这其实是一个定理——朱刘定理);

    附上代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define ll long long
    using namespace std;
    const int N = 2e5+10;
    const int mod = 1e9+7;
    
    inline int read(){
        int ref=0,x=1;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) ref=ref*10+ch-'0',ch=getchar();
        return ref*x;
    }
    
    int n,m,xx,yy;
    struct edge{
        int next,to;
    }a[N<<4];
    int cnt,head[N<<4],x[N],y[N];
    int ru[N],vis[N],f[N];
    ll ans=1,sum=1;
    
    void add(int u,int v){
        a[++cnt]=(edge){head[u],v};
        head[u]=cnt;
    }
    
    ll ksm(ll a,ll b){
        ll rel=1;
        a%=mod;
        while(b){
            if(b&1) rel=(rel*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        return rel;
    }
    
    ll inv(ll a){ return ksm(a,mod-2); }
    
    void dfs(int x){
        if(vis[x]) return ;
        vis[x]=1;
        if(x==yy){
            f[x]=(sum*inv(ru[x]))%mod;
            return ;
        }
        for(int i=head[x];i;i=a[i].next){
            int v=a[i].to;
            dfs(v);
            f[x]=(f[x]+f[v])%mod;//乘法具有分配律,不影响最终的答案 
        }
        f[x]=(f[x]*inv(ru[x]))%mod;
    }
    
    int main()
    {
        n=read(),m=read(),xx=read(),yy=read();
        ru[1]++;
        for(int i=1;i<=m;++i){
            int x,y;
            x=read(),y=read();
            add(y,x);
            ru[y]++;
        }
        for(int i=1;i<=n;++i){
            if(i==yy) ans=(ans*(ru[i]+1))%mod;
            else ans=(ans*ru[i])%mod;
            sum=(sum*ru[i])%mod;
        }
        dfs(xx);
        if(xx==yy||yy==1) printf("%lld",sum%mod);
        else printf("%lld",(ans-f[xx]+mod)%mod);
        return 0;
    }
  • 相关阅读:
    Python操作文件和目录
    ffmpeg命令简单使用
    【转载】一个简单的爬虫:爬取豆瓣的热门电影的信息
    【转载】正则表达式re.S的用法
    linux用户添加
    SQL语句update修改数据库字段
    linux命令之cp
    linux命令——tree命令
    Linux磁盘管理
    Python资源安装过程出现Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None))…………
  • 原文地址:https://www.cnblogs.com/nnezgy/p/11469152.html
Copyright © 2011-2022 走看看