codeforces 449D

题目

给你 1e6 个数,你需要找出有多少子序列,他们的值 and 起来为0。

思路

f(s) 表示状态为 s s 中为1的位一定是1,为0的位可能为1的可选数字的个数, g(s) 表示状态 s 中为1的位的个数。那么可以由容斥原理得到 ans=220s=0(1)g(s)(2f(s)1)

核心就在于计算 f(s) 的值,若暴力计算,需要枚举 s 的子集复杂度高达 320 ,不可行。考虑 dp[i][s] 表示低 i 位为 s0 ,高 (19i) 位为 s1 的数的个数,其中, s0 中为1的一定为1,为0的可能为1, s1 中为1的一定为1,为0的一定为0。

转移显然,

dp[i][s]={dp[i1][s], dp[i1][s]+dp[i1][s|1<<(i1)],si=1si=0

//
//  Created by Running Photon
//  Copyright (c) 2015 Running Photon. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const int maxn = 1025 * 1025;
const int maxv = 1e3 + 10;
const double eps = 1e-9;

ll dp[21][maxn];
ll Pow(ll a, ll n) {
    ll ret = 1;
    while(n) {
        if(n & 1) ret = ret * a % MOD;
        n >>= 1;
        a = a * a % MOD;
    }
    return ret;
}
int main() {
#ifdef LOCAL
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt", "r", stdin);
    freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);
#endif
//  ios_base::sync_with_stdio(0);
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        int a;
        scanf("%d", &a);
        dp[0][a]++;
    }
    for(int i = 1; i <= 20; i++) {
        for(int s = 0; s < 1 << 20; s++) {
            dp[i][s] = dp[i-1][s];
            if((s >> (i - 1) & 1) == 0) dp[i][s] += dp[i-1][s | (1 << i - 1)];
            assert(dp[i][s] >= 0);
        }
    }
    ll ret = 0;
    for(int i = 0; i < 1 << 20; i++) {
        int num = __builtin_popcount(i);
        if(num & 1) ret = (ret + MOD - Pow(2, dp[20][i]) + 1) % MOD;
        else ret = (ret + Pow(2, dp[20][i]) - 1 + MOD) % MOD;
    }
    cout << ret << endl;
    return 0;
}

你可能感兴趣的:(动态规划,数学)