CF449D Jzzhu and Numbers

在这里插入图片描述
传送门
很有意思的题目,考验对容斥的熟悉程度和对状压dp的理解。
首先我们再在翻译的基础上转化一下题意。
设f[i]为恰好i个1,g[i]为至少i个1。
也就是一个子集满足&的结果1的个数恰好为0个。因为至少0个的包含至少一个的,形成了容斥。
  f [ 0 ] = ∑ i = 0 ( − 1 ) i g [ i ] \ f[0] = \sum_{i=0}(-1)^ig[i]  f[0]=i=0(1)ig[i]
故我们的任务变成了统计g[i]。这时候用SOS dp,令形如i&k==i的像这样进行转移dp[i]+=dp[k],即1少的包含1多的。初始化在读入进行。


//
// Created by SANZONG on 2021/7/21.
//
#include "bits/stdc++.h"

#define int long long
using namespace std;
const int N = 5e6 + 10;
const int mod = 1e9 + 7;
int m;
int a[N];
int pos[N];
int rev[N];
int f[N];
int p[N];
signed main() {
    
    
    int n;
    cin >> n;
    p[0] = 1;
    for (int i = 1; i <= N; ++i) {
    
    
        p[i] = p[i-1] * 2 % mod;
    }
    for (int i = 1; i <= n; ++i) {
    
    
        cin >> a[i];
        f[a[i]]++;
    }
    for (int j = 0; (1 << j) <= 1e6; ++j) {
    
               //必须先枚举位,否则会造成交叉重复计算。一个数会由多个多一个1的转来。
        for (int i = 1e6; i >= 0; --i) {
    
    
            if (i & (1 << j)) {
    
    
                (f[i ^ (1 << j)] += f[i])%=mod;
            }
        }
    }
    int ans = 0;
    for (int i = 0; i <= 1e6; ++i) {
    
    
        if (f[i]) {
    
    
            int t = 1;
            if (__builtin_popcount(i) & 1) {
    
    
                t = -1;
            }
            (ans += (t * (p[f[i]] - 1))%mod + mod)%=mod;
        }
    }
    cout << (ans + mod) % mod << endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_45509601/article/details/119039117