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;
    }
  • 相关阅读:
    启动或重启Oracle数据以及监听
    占坑 对自己的目标要求
    线程的使用经验(包括 Thread/Executor/Lock-free/阻塞/并发/锁等)
    接口的调用Client测试
    解决ftp登录问题:500 OOPS: cannot change directory:/home/xxx 500 OOPS: child died
    Tomcat7性能调优
    mysql性能调优
    四:JVM调优原理与常见异常处理方案
    redis的线程模型 与 压力测试
    为什么要用消息队列 及 自己如何设计一个mq架构
  • 原文地址:https://www.cnblogs.com/nnezgy/p/11469152.html
Copyright © 2011-2022 走看看