zoukankan      html  css  js  c++  java
  • [HNOI2015][bzoj4011] 落叶枫音 [拓扑DP]

    题面

    传送门

    思路

    首先有一个结论(应该是有比较大的利用价值的):

    有向无环图的生成外向树树个数等于所有入度非0的点的入度乘积

    然后这道题里面,唯一不合拍的因素就是这里有一条可能成环的边

    我们可以把这条边先加入原来的DAG里面,然后用上面的结论算出总生成树个数,再减去不合法的,得到答案

    那么有哪些方案是不合法的呢?

    显然,我们加入一条边形成了环的话,我们前面的计算会算上选择包含新加入的边的某个环的方案,而这个方案是不合法的

    假设新加入的边是s->t,那么我们只要找到所有包含t->s的生成树,然后减掉,就得到了答案

    具体而言,这里的操作方法,是在原来的DAG上面dp

    $dp[t]$初始化为原来的答案,然后往$s$做拓扑序上的$dp$

    转移方程是$dp[v]=sum frac{dp[u]}{in[u]}$,其中$in[u]$表示原图中的入度

    显然只有能够在$t->s$路径上的点的$dp$值才有意义,而这里除掉度数表示的就是这里仅能选一条边

    最后用原来答案减掉$dp[s]$即可

    Code

    #include<iostream>
    #include<cstring> 
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cassert>
    #define ll long long
    #define MOD 1000000007
    using namespace std;
    inline int read(){
        int re=0,flag=1;char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
        return re*flag;
    }
    int n,m,op,ed,first[100010],cnte;ll in[100010],inv[200010],re[100010];
    struct edge{
        int to,next;
    }a[200010];
    inline void add(int u,int v){
        a[++cnte]=(edge){v,first[u]};first[u]=cnte;
    }
    ll ans=1,dp[100010];
    int q[100010],head,tail;
    void topo(){
        int i,u,v;head=tail=0;
        dp[ed]=ans;in[ed]--;
        for(i=1;i<=n;i++){
            if(!in[i]) q[tail++]=i;
            re[i]=in[i];
        }
        in[ed]++;
        while(head<tail){
            u=q[head++];dp[u]=dp[u]*inv[in[u]]%MOD;
            for(i=first[u];~i;i=a[i].next){
                v=a[i].to;re[v]--;
                dp[v]=(dp[v]+dp[u])%MOD;
                if(!re[v]) q[tail++]=v;
            }
        }
    }
    int main(){
        memset(first,-1,sizeof(first));
        n=read();m=read();op=read();ed=read();in[ed]++;
        int i,t1,t2;
        inv[1]=1;
        for(i=2;i<=m;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
        for(i=1;i<=m;i++){
            t1=read();t2=read();
            add(t1,t2);in[t2]++;
        }
        for(i=2;i<=n;i++) ans=ans*in[i]%MOD;
        if(ed==1){
            printf("%lld
    ",ans);return 0;
        }
        topo();
        printf("%lld
    ",(ans-dp[op]+MOD)%MOD);
    }
  • 相关阅读:
    octotree神器 For Github and GitLab 火狐插件
    实用篇如何使用github(本地、远程)满足基本需求
    PPA(Personal Package Archives)简介、兴起、使用
    Sourse Insight使用过程中的常使用功能简介
    Sourse Insight使用教程及常见的问题解决办法
    github 遇到Permanently added the RSA host key for IP address '192.30.252.128' to the list of known hosts问题解决
    二叉查找树的C语言实现(一)
    初识内核链表
    container_of 和 offsetof 宏详解
    用双向链表实现一个栈
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9603078.html
Copyright © 2011-2022 走看看