CF449D Jzzhu and Numbers

给出一个长度为n的序列\(a_1,a_2...a_n\)。求构造出一个序列\(i_1 \le i_2 \le ... \le i_k(1\le{k}\le{n})\)使得\(a_{i_1}\&a_{i_2}\&...\&a_{i_k}=0\) 。求方案数模\(10^9+7\)

也就是从\(\{a_i\}\) 里面选出一个非空子集使这些数按位与起来为0.

\(n\le 10^6,a_i\le10^6\)


发现这个就是个与意义下的背包,然后考虑用 FWT 优化,我们每次加入一个物品之后会有两种选择,也就是会使其 \(\times2\) ,那么用桶记录下 \(i\) 有多少个,然后 FWT 过去,这里的 \(A_i\) 就是每个位置上的翻倍的倍数了,算出来之后再 IFWT 回去就可以了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 20;
const int p = 1e9 + 7;
using namespace std;
int n,a[1 << N],t[1 << N],m2[1 << N];
void FWT(int *a)
{
    for (int i = 1;i < (1 << N);i <<= 1)
        for (int j = 0;j < (1 << N);j += i << 1)
            for (int k = 0;k < i;k++)
            {
                int a0 = a[j + k],a1 = a[j + k + i];
                a[j + k] = a1;
                a[j + k + i] = (a0 + a1) % p;
            }
}
void IFWT(int *a)
{
    for (int i = 1;i < (1 << N);i <<= 1)
        for (int j = 0;j < (1 << N);j += i << 1)
            for (int k = 0;k < i;k++)
            {
                int a0 = a[j + k],a1 = a[j + k + i];
                a[j + k] = (-a0 + a1) % p;
                a[j + k + i] = a0;
            }
}
int main()
{
    scanf("%d",&n);
    m2[0] = 1;
    for (int i = 1;i < (1 << N);i++)
        m2[i] = 2ll * m2[i - 1] % p;
    for (int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
        t[a[i]]++;
    }
    FWT(t);
    for (int i = 0;i < (1 << N);i++)
        t[i] = m2[t[i]];
    IFWT(t);
    cout<<(t[0] + p) % p<<endl;
    return 0;
}
posted @ 2021-01-18 20:49  eee_hoho  阅读(78)  评论(0编辑  收藏  举报