zoukankan      html  css  js  c++  java
  • luogu3244 bzoj4011 HNOI2015 落忆枫音

    这道题目题面真长,废话一堆。

    另外:这大概是我第一道独立做出来的HNOI2011年以后的题目了吧。像我水平这么差的都能做出来,dalao您不妨试一下自己想想?

    题目大意:给一个DAG,其中1号点没有入度,现在新加入一条不重复的边,使得它可能有环。求它的生成子图个数,使得子图正好包含N-1条边且1号点与其它的所有点连通。

    题目分析:

    我们首先要发现这是一个树的结构!有向的树。

    分析树的特点,树的父亲只有一个,我们不妨从这里入手。

    在这一个生成子图中,谁是谁的父亲?

    我们知道1号点一定是root,这是为什么呢?

    原因在于一号点没有入度,我们就认为了它是root。

    那么假设有一条x->y的路径,那么x就是y的父亲,这样的定义是合理的。

    考虑不加边之前的情况,图是一个DAG,我们求它的生成树(姑且这么叫吧)。

    根据我们上面的分析,我们猜想:除一号点外所有点的入度乘积为该图的生成树数量。

    证明如下:

    首先我们考虑这个是怎么来的。每个点选择一个父亲,则根据乘法原理得到上面的猜想。

    那么我们的命题是:每个点选一个父亲,则这个方案必定合法。

    我们一共对N-1个点选了到fa的边,每次选择都合并了两个点,共合并了N-1次,因此共N个点被合并,又由于不存在环的情况,所以这样的方案是合法的。

    考虑多了一条边的情况。那么我们由于已经求得了所有不包含这条边的情况,我们只需要分析多了这条边的情况。

    假设这是一条从x到y的边。当这条边必选的时候,y点必定不被考虑。因为它的父亲已经确定。

    那么我们对剩下的点重新做一遍乘法,得到了ans2。

    考虑答案多了什么。

    当选出的边与当前边成环的时候,这个并不是一个生成树,我们没办法解决了吗?当然不是。

    我们不妨单独拿出一个环考虑。当形成了这个环的时候,对答案的影响是多少。实际上这不难统计,把剩下的点的入度全部乘起来就是答案。嘿嘿,这不就是总和除以现在的点数吗。

    那么我们考虑求y到x的路径的点的入度的逆,DP一发轻松解决。然后乘法分配率证明正确性。

    后记:这题我先写了个错的然后发现我想错了,答案小了,调了调变大了,然后又调,结果搜索出了正解233。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int maxn = 102000;
    const int mod = 1000000007;
    int n,m,x,y;
    ll ans,ans2;
    int in[maxn],arr[maxn];
    ll dist[maxn],inv[maxn];
    vector <int> g[maxn];
    vector <int> ng[maxn];
    
    ll fast_pow(int now,int p){
        if(p == 1) return now;
        if(p == 0) return 1;
        ll z = fast_pow(now,p/2); z*=z; z %= mod;
        if(p & 1){z*=now;z%=mod;}
        return z;
    }
    
    void read(){
        scanf("%d%d%d%d",&n,&m,&x,&y);
        for(int i=1;i<=m;i++){
            int a,b; scanf("%d%d",&a,&b);
            g[a].push_back(b);in[b]++;
            ng[b].push_back(a);
        }
        for(int i=2;i<=n;i++) inv[i] = fast_pow(in[i],mod-2);
    }
    
    void dfs(int now){
        if(arr[now]) return;
        arr[now] = 1;
        for(int i=0;i<ng[now].size();i++){
            dfs(ng[now][i]);
            dist[now] += (dist[ng[now][i]]*inv[now])%mod;
            dist[now] %= mod;
        }
    }
    
    void work(){
        ans = ans2 = 1;
        for(int i=2;i<=n;i++) ans = (ans*in[i]) % mod;
        if(x == y){printf("%lld
    ",ans);return;}
        if(y == 1){printf("%lld
    ",ans);return;}
        for(int i=2;i<=n;i++) if(i != y) ans2 = (ans2*in[i]) % mod;
        in[y]++;inv[y] = fast_pow(in[y],mod-2);dist[y] = inv[y];arr[y] = 1;dfs(x);
        ans += ans2; ans %= mod;
        ans -= (ans*dist[x])%mod; ans += mod; ans %= mod;
        printf("%lld
    ",ans);
    }
    
    int main(){
        read();
        work();
        return 0;
    }
  • 相关阅读:
    jenkins代码自动部署
    jenkins安装
    git图形管理工具
    gitlab自动备份恢复与卸载
    linux下获取外网IP
    网站安全webshell扫描
    jQuery动画效果实现
    form表单中的enctype属性什么意思?
    你那么努力又怎么样!
    话语
  • 原文地址:https://www.cnblogs.com/1-1-1-1/p/8596753.html
Copyright © 2011-2022 走看看