Codeforces.449D.Jzzhu and Numbers(容斥 高维前缀和)

题目链接

\(Description\)

给定\(n\)个正整数\(a_i\)。求有多少个子序列\(a_{i_1},a_{i_2},...,a_{i_k}\),满足\(a_{i_1},a_{i_2},...,a_{i_k}\) \(and\)起来为\(0\)
\(n\leq10^6,\quad 0\leq a_i\leq10^6\)

\(Solution\)

这个数据范围。。考虑按位容斥:
\(g_x\)表示\(x\)的二进制表示中\(1\)的个数,\(f_x\)表示有多少个\(a_i\)满足\(a_i\&x=x\)
想要让选出来的子序列最终\(and\)和为\(x\),那么只能从这\(f_x\)个数中选。
所以\(Ans=\sum_{x=0}^{lim}(-1)^{g_x}(2^{f_x}-1)\)

那么如何求\(f_x\)
\(a_i\&x=x\),即\(x\)\(a_i\)的子集,所以对\(f_x\)枚举超集更新即可。复杂度\(O(2^nn)\)

注意因为写法问题数组要开两倍。

又一不小心一个rank1...

//62ms  35500KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mod 1000000007
#define lb(x) (x&-x)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
typedef long long LL;
const int N=3e6+5;

int bit[N],pw[N],f[N];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}

int main()
{
    int n=read(),lim=0;
    for(int t,i=1; i<=n; ++i) ++f[t=read()],lim=std::max(lim,t);
    pw[0]=1;
    for(int i=1; i<=n; ++i) pw[i]=pw[i-1]<<1, pw[i]>=mod&&(pw[i]-=mod);
    for(int i=0; 1<<i<=lim; ++i)
        for(int s=0; s<=lim; ++s)
            if(!(s>>i&1)) Add(f[s],f[s|(1<<i)]);
    LL ans=0;
    for(int i=1; i<=lim; ++i) bit[i]=bit[i^lb(i)]^1;
    for(int i=0; i<=lim; ++i) ans+=bit[i]?mod-pw[f[i]]+1:pw[f[i]]-1;
    printf("%I64d\n",ans%mod);

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10073986.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值