zoukankan      html  css  js  c++  java
  • [hdu 6355] Fireflies

    题意

    在一个(n)维有限超立方体(((1, p_1), (1, p_2), ... , (1, p_n)))中,你可以在若干个位置放一只萤火虫。

    萤火虫可以行动若干次,每一次从((x_1, x_2, x_3,...,x_n))走向((y_1, y_2, y_3,...,y_n)),都要满足:

    1.起点和终点要在超立方体内;

    2.(forall_i x_i le y_i)

    3.((sum y_i - x_i) = 1)

    求最开始最少放多少个,才能确保存在一种方案,对于每一个单位空间,至少用一只萤火虫遍历它。

    题解

    最优解的结构可以用Dilworth定理来分析。

    Dilworth定理表明,最小链覆盖数等于最大反链数,也即选出一个集合,使得集合中的位置两两不可达,要最大化这个集合的大小。

    Sperner定理表明,若要选择一个集合(S)的幂集的一个子集使得其中没有一个集合包含在另一个集合中,则这种子集的大小最大为(inom {|S|}{lfloor |S| / 2 floor})

    则Sperner定理的一个推广)恰好能给出本问题的解:

    选择(n)维坐标之和为(M = lfloor frac {1}{2} sum_{i = 1} ^ n (p_i + 1) floor)的位置即可达到最大的数量。

    则现在问题转化为求(sum_{i = 1} ^ n x_i = M)(1 le x_i le p_i)的解的数量。

    这是个经典问题,可以用容斥来做。

    但考虑到(n le 32),不能直接容斥。

    容易想到可能会用到meet-in-middle。但是如何用中途相遇法呢?

    考虑到如果我们容斥时选出集合(S)代表(S)中的(x_i)都至少比(p_i)大,方案数就是

    [max(0, inom{M - 1 - sum_{i in S} p_i}{n - 1}) ]

    考虑到(n)很小,然后就有一个套路,将这个组合数看成下降幂形式,然后就成了(sum_{i in S} p_i)(n - 1)次多项式。那么我们可以先预处理出这个多项式的系数,然后在处理出每个集合(S)(sum_{i in S} p_i)(这里的(S)是折半枚举的),然后meet-in-middle的时候合并一下,用个前缀和优化就好了。

    注意到如果(M - 1 - sum_{i in S}p_i < 0),不能用下降幂来算,这种情况方案数应该算作(0)(实际上在这个问题中(inom {n}{m})只要(n < m)就是(0))。处理时,可以用一个双指针来搞一搞这部分。

    最终复杂度大概是(O(2 ^ {n / 2} n ^ 2))

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N = 40, mo = 1e9 + 7;
    const int M = 1 << 16 | 5;
    inline int read () {
        static int x;
        scanf("%d", &x);
        return x;
    }
    inline ll exp (ll a, ll b, ll ret = 1) {
        for (a %= mo; b; b >>= 1, a = a * a % mo)
            if (b & 1) ret = ret * a % mo;
        return ret;
    }
    
    int n, m, a[N];
    int bitcnt[M];
    ll k, cb[N][N], ivf[N];
    ll f[N][N], coe[N];
    
    void prework () {
        ivf[0] = 1;
        for (int i = 1; i < N; ++i)
            ivf[i] = ivf[i - 1] * exp(i, mo - 2) % mo;
        bitcnt[0] = 0;
        for (int i = 1; i < M; ++i)
            bitcnt[i] = bitcnt[i >> 1] + (i & 1);
        cb[0][0] = 1;
        for (int i = 1; i < N; ++i) {
            cb[i][0] = cb[i][i] = 1;
            for (int j = 1; j < i; ++j)
                cb[i][j] = (cb[i - 1][j - 1] + cb[i - 1][j]) % mo;
        }
    }
    void predo () {
        memset(f, 0, sizeof f);
        f[0][0] = n & 1 ? 1 : -1;
        for (int i = 1; i < n; ++i) {
            for (int j = 1; j < n; ++j)
                f[i][j] = f[i - 1][j - 1];
            for (int j = 0; j < n; ++j)
                f[i][j] = (f[i][j] - f[i - 1][j] * ((k - i) % mo) % mo + mo) % mo;
        }
        for (int i = 0; i < n; ++i)
            coe[i] = f[n - 1][i] * ivf[n - 1] % mo;
    }
    
    pair <ll, vector <ll> > p[M], q[M];
    ll presum[N];
    ll in_ex (ll ret = 0) {
        for (int s = 0; s < (1 << m); ++s) {
            ll sum = 0; int sgn = bitcnt[s] & 1 ? -1 : 1;
            for (int i = 0; i < m; ++i)
                if (s >> i & 1) sum += a[i];
            p[s].first = sum, p[s].second.resize(n, 0);
            for (int i = 0; i < n; ++i)
                p[s].second[i] = exp(sum % mo, i) * sgn % mo;
        }
        sort(p, p + (1 << m));
        for (int s = 0; s < (1 << (n - m)); ++s) {
            ll sum = 0; int sgn = bitcnt[s] & 1 ? -1 : 1;
            for (int i = 0; i < (n - m); ++i)
                if (s >> i & 1) sum += a[i + m];
            q[s].first = sum, q[s].second.resize(n, 0);
            for (int i = 0; i < n; ++i)
                q[s].second[i] = exp(sum % mo, i) * sgn % mo;
        }
        sort(q, q + (1 << (n - m)));
        memset(presum, 0, sizeof presum);
        for (int s = (1 << m) - 1, t = 0; ~s; --s) {
            for ( ; t < (1 << (n - m)) && k - n - (p[s].first + q[t].first) >= 0; ++t) {
                for (int i = 0; i < n; ++i) presum[i] = (presum[i] + q[t].second[i]) % mo;
            }
            for (int i = 0; i < n; ++i) {
                ll tmp = 0;
                for (int j = 0; j <= i; ++j)
                    tmp = (tmp + cb[i][j] * p[s].second[j] % mo * presum[i - j] % mo) % mo;
                ret = (ret + coe[i] * tmp) % mo;
            }
        }
        return (ret % mo + mo) % mo;
    }
    signed main () {
        prework();
        for (int _ = read(); _; --_) {
            n = read(), m = (n + 1) / 2, k = 0;
            for (int i = 0; i < n; ++i)
                a[i] = read(), k += a[i] + 1;
            k /= 2;
            if (n == 1) {
                printf("1
    ");
                continue;
            }
            predo();
            printf("%lld
    ", in_ex());
        }
        return 0;
    }
    
  • 相关阅读:
    zoj3299 Fall the Brick
    hdu4533 威威猫系列故事——晒被子
    FZU 1650 1752 a^b mod c
    Codeforces Round #136 (Div. 1) B. Little Elephant and Array
    Codeforces Round #292 (Div. 1) C. Drazil and Park
    Uva 12436 Rip Van Winkle's Code
    Codeforces Beta Round #19 D. Points
    hdu1513 Palindrome
    poj1160 Post Office
    zjnu1181 石子合并【基础算法・动态规划】——高级
  • 原文地址:https://www.cnblogs.com/psimonw/p/10727408.html
Copyright © 2011-2022 走看看