zoukankan      html  css  js  c++  java
  • hihocoder #1071 : 小玩具

    闻所未闻的$dp$神题(我不会的题)

    令$f[S][i]$表示子集状态为$S$,且$S$中最大联通块恰好为$i$的方案数

    考虑转移,我们枚举$S$中最小的元素$v$来转移,这样就能不重

    $f[S][i] = sumlimits_{T in S ;and;v in T} f[T][...] * C[S wedge T]$

    由于这么递归转移不好确定后面的状态,因此我们可以递推转移,在代码中有所体现

    $C[S]$表示将$S$联通的方案数

    我们考虑容斥,用全集减去所有不联通的方案数,我们考虑枚举最小点$v$所在的集合

    之后转移时$C[S] = 2^{E[S]} - sumlimits_{T in s ;ans;v; in T} C[T] * 2^{E[S wedge T]}$

    其中,$E[S]$表示处于$S$内部的边的方案数

    复杂度$O(3^n * n)$

    #include <set>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define re register
        #define de double
        #define le long double
        #define ri register int
        #define ll long long
        #define sh short
        #define pii pair<int, int>
        #define mp make_pair
        #define pb push_back
        #define fi first
        #define se second
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            
            return p * w;
        }
        int wr[50], rw;
        #define pc(iw) putchar(iw)
        tpr inline void write(ra o, char c = '
    ') {
            if(!o) pc('0');
            if(o < 0) o = -o, pc('-');
            while(o) wr[++ rw] = o % 10, o /= 10;
            while(rw) pc(wr[rw --] + '0');
            pc(c);
        }
        tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; }
        tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } 
        tpr inline bool ckmin(ra &a, ra b) { return (a > b) ? a = b, 1 : 0; }
        tpr inline bool ckmax(ra &a, ra b) { return (a < b) ? a = b, 1 : 0; }
    }
    using namespace std;
    using namespace remoon;
    
    #define sid 17
    #define mod 1000000007
    
    inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
    inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; }
    inline int mul(int a, int b) { return 1ll * a * b % mod; }
    
    int pc[2555];
    int u[555], v[555];
    int E[(1 << 16) + 5000], C[(1 << 16) + 5000];
    int f[(1 << 16) + 5000][sid];
    int n, m;
    
    int main() {
        
        n = read(); m = read(); pc[0] = 1;
        rep(i, 1, 1000) pc[i] = mul(pc[i - 1], 2);
        rep(i, 1, m) u[i] = read(), v[i] = read();
        
        rep(S, 0, (1 << n) - 1) rep(i, 1, m) 
        if((S & (1 << u[i] - 1)) && (S & (1 << v[i] - 1))) ++ E[S];
            
        C[0] = 1;
        rep(S, 1, (1 << n) - 1) {
            int res = pc[E[S]], mi = -1; 
            rep(i, 1, n) if(S & (1 << i - 1)) { mi = i; break; }
            for(ri T = S & (S - 1); T; T = (T - 1) & S)
            if(T & (1 << mi - 1)) dec(res, mul(C[T], pc[E[S ^ T]]));
            C[S] = res;
        }
            
        f[0][0] = 1;
        rep(S, 0, (1 << n) - 1) rep(j, 0, n) if(f[S][j]) {
            int mi = -1;
            rep(i, 1, n) if(!(S & (1 << i - 1))) { mi = i; break; }
            if(mi == -1) continue;
            int D = ((1 << n) - 1) ^ (S | (1 << mi - 1));
            for(ri T = D; ; T = (T - 1) & D) {
                int st = __builtin_popcount(T | (1 << mi - 1));
                inc(f[S | (1 << mi - 1) | T][max(j, st)], mul(f[S][j], C[(1 << mi - 1) | T]));
                if(!T) break;
            }
        }
        
        rep(i, 1, n) 
        write(f[(1 << n) - 1][i]);
        
        return 0;
    }
  • 相关阅读:
    typescript泛型
    安卓手机IPhone抓包Https
    js里面for循环的++i与i++
    http请求头
    从浏览器地址栏输入url到页面呈现
    docker部署nodejs应用
    mac下使用MongoDB
    使用vue-cli3&vue ui图形化界面创建项目
    javascript中的call, apply(转载)
    跨域请求
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9800998.html
Copyright © 2011-2022 走看看