zoukankan      html  css  js  c++  java
  • Topcoder 15279 SpanningSubgraphs

    题目链接

    Topcoder 15279 SpanningSubgraphs

    题目大意

    有一张 (n) 个点,(m) 条边的图,对于每个 (kin[n-1,m]),求出大小为 (k) 的能使图连通的边集数量。

    (1leq nleq 15)(1leq mleq 200)

    思路

    考虑朴素的容斥,设 (siz_S) 为两个端点都在点集 (S) 中的边数,(dp_{S,k}) 为在点集 (S) 中满足条件的 (k) 元边集的数量。经过简单思考可以得到转移式:

    [dp_{S,k}=inom{siz_S}{k}-sum_{Tsubset S,lowbit(S)in T}sum_{i=0}^k dp_{T,i}cdot inom{siz_{S-T}}{k-i} ]

    为了去重,这里钦定 (T) 中包含 (S) 的最小元素。由于每次要枚举子集和边数,这么做是 (O(3^nm^2)) 的,(5.7e9) 运算量显然太大了,需要优化。

    接下来的思路参考了 Petr Mitrichev 的 博客,这个转移式已经没有什么优化的余地了,而且 (dp) 的两维状态也不可省略,于是我们只能优化计算。这里的突破口在于组合数上,通常计算时我们会将组合数预处理好直接带入,但是现在我们要 拆开组合数,在计算组合数的同时把转移式的值一起算好。转移式的 (S) 不可忽略,我们枚举 (S),对于每个 (S) 进行一次如下的计算:

    引入一个辅助数组 (g_{i,j})(i)(S) 中边数,(j)(T) 中的边数。初始令 (g_{i,j}=sum_{siz_{S-T}=j}dp_{T,i}),这些集合 (T)(S) 的组合数贡献是相同的,在带上系数的情况下,用 (g) 算组合数:

    [g_{i,j-1} leftarrow g_{i,j};,;; g_{i+1,j-1}leftarrow g_{i,j} ]

    注意到 (siz_T) 的范围各不相同,为了统一结果,第二维要从高往低算。于是 (g_{i,0}) 即为先前我们所需要的式子,原先的转移式简化成了 (dp_{S,k}=inom{siz_S}{k}-g_{i,0})

    (g_{i,j}) 初始化 (O(3^nm)) ,计算 (O(2^nm^2))(dp) 转移直接降到了 (O(2^n)),所以总复杂度 (O(3^nm+2^nm^2)),计算量 (3e8),我们可以通过只枚举包含 (1) 的连通块再加速一点,可以通过此题。

    Code

    #include<iostream>
    #include<vector>
    #include<cstring>
    #define mem(a,b) memset(a, b, sizeof(a))
    #define rep(i,a,b) for(int i = (a); i <= (b); i++)
    #define per(i,b,a) for(int i = (b); i >= (a); i--)
    #define N 15
    #define M 205
    #define mod 1000000007
    using namespace std;
    
    int C[M][M], siz[1<<N], f[1<<N][M], g[M][M];
    
    class SpanningSubgraphs{
        public:
        void init(int n){
            rep(i,0,n){
                C[i][0] = 1;
                rep(j,1,i) C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
            }
        }
    
        vector<int> count(int n, vector<int> a, vector<int> b){
            int m = a.size();
            int all = (1<<n)-1;
            init(m);
            rep(i,0,all){
                rep(j,0,m-1)
                    if((1<<a[j]&i) && (1<<b[j]&i)) siz[i]++;
                rep(j,0,siz[i]) f[i][j] = C[siz[i]][j];
            }
    
            rep(set,1,all){
                mem(g, 0);
                int tot = set^1;
                for(int sub = tot&(tot-1); ; sub = (sub-1)&tot){
                    if((sub^1) == set) break;
                    rep(i,0,siz[set]) (g[i][siz[set^sub^1]] += f[sub^1][i]) %= mod;
                    if(!sub) break;
                }
                
                rep(i,0,siz[set]) per(j,siz[set],1)
                    (g[i][j-1] += g[i][j]) %= mod, (g[i+1][j-1] += g[i][j]) %= mod;
    
                rep(i,0,siz[set]) (f[set][i] += mod-g[i][0]) %= mod;
                set++;
            }
            vector<int> ans;
            rep(i,n-1,m) ans.push_back(f[all][i]);
            return ans;
        }
    } solve;
    
    int main(){
        int n, m, u;
        vector<int> a, b;
        cin>>n>>m;
        rep(i,1,m) cin>>u, a.push_back(u);
        rep(i,1,m) cin>>u, b.push_back(u);
        vector<int> ans = solve.count(n, a, b);
        for(int k : ans) cout<<k<<" ";
        cout<<endl;
        return 0;
    }
    
  • 相关阅读:
    要想成为前端大神,那些你不得不知晓的web前端命名规范。
    正确开启Mockjs的三种姿势:入门参考(一)
    1024码农节-向自己致敬!
    ES6 常用总结(前端开发js技术进阶提升总结)
    JS快速构建数组方法
    React绑定事件动态化的实现方法
    JQ遇到$(‘.xxx’).attr(‘display’)一直返回undefined
    你所要掌握的最简单基础的React渲染优化
    MyBatis Generator
    Spring boot集成redis初体验
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/15005887.html
Copyright © 2011-2022 走看看