CF449D Jzzhu and Numbers 高维前缀和

title

CF449D

Description

给出一个长度为 \(n\) 的序列 \(a_1,a_2...a_n\) 。

求构造出一个序列 \(i_1 \le i_2 \le ... \le i_k\) 使得 \(a_{i_1}\&a_{i_2}\&...\&a_{i_k}=0\) 。求方案数模 \(10^9+7\) 。

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

analysis

这不是普转提的第四题吗?不得不防啊。

这也是一个新的知识点,高维前缀和。

从最开始的说起:

一维前缀和:

for (int i=1; i<=n; ++i) a[i]+=a[i-1];

二维前缀和:

for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];

也可以写成这样:

for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j) a[i][j]+=a[i-1][j];
for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j) a[i][j]+=a[i][j-1];

好像第二种写法有些麻烦?别急,看三维的:

for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j)
        for (int k=1; k<=n; ++k)
            a[i][j][k]+=a[i-1][j][k]+a[i][j-1][k]+a[i][j][k-1]
                    -a[i][j-1][k-1]-a[i-1][j][k-1]-a[i-1][j-1][k]
                    +a[i-1][j-1][k-1];

换成第二种来写一下:

for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j)
        for (int k=1; k<=n; ++k) a[i][j][k]+=a[i-1][j][k];
for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j)
        for (int k=1; k<=n; ++k) a[i][j][k]+=a[i][j-1][k];
for (int i=1; i<=n; ++i)
    for (int j=1; j<=n; ++j)
        for (int k=1; k<=n; ++k) a[i][j][k]+=a[i][j][k-1];

分析一下:对于三维前缀和,第一种写法的复杂度是 \(O(8N^3)\) ,而第二种写法的复杂度为 \(O(3N^3)\) ,快了很多。

而且随着维度增高,第二种写法的愈发加大。

最后的解决方法便是高维前缀和,第一层循环从低到高枚举二进制位数,第二层循环从 \(1\) 到 \(n\) 枚举所有元素(下面给出本题中的高维前缀和 \(code\) ):

for (int i=0; i<=20; ++i)
   for (int j=1; j<=maxn; ++j)
       if (j&(1<<i)) f[j^(1<<i)]+=f[j];

对于本题而言,最后的方案数为: \(2^{n-1}-至少1位为1+至少2位为1-至少3位为1...\) 。

根据上道题的经验,设 \(f[x]\) 表示包含 \(i\) 这个状态的数的个数。

然后枚举所有可能的有 \(1\) 的情况:\(ans=2^{n-1}-\sum(-1)^{|S|}*2^{f[i]-1}\) 。其中 \(|S|\) 表示这种状态 \(i\) 中含 \(1\) 的个数。

\(f[x]\) 就用刚才的高维前缀和解决就好了,然后应该就没了。

code

#include<bits/stdc++.h>

typedef long long ll;
const int maxn=1e6,mod=1e9+7;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

int p[maxn+10],f[maxn+10];
int main()
{
    int n;read(n);
    for (int i=1,x; i<=n; ++i) read(x), ++f[x];
    p[0]=1;
    for (int i=1; i<=maxn; ++i) p[i]=1ll*p[i-1]*2%mod;

    for (int i=0; i<=20; ++i)
        for (int j=1; j<=maxn; ++j)
            if (j&(1<<i)) (f[j^(1<<i)]+=f[j])%=mod;
    int ans=0;
    for (int i=0; i<=maxn; ++i)
    {
        int opt=1;
        for (int j=0; j<=20; ++j)
            if (i&(1<<j)) opt=-opt;
        ans=(ans+(1ll*opt*(p[f[i]]-1)%mod)+mod)%mod;
    }
    write(ans,'\n');
    IO::flush();
    return 0;
}

原文地址:https://www.cnblogs.com/G-hsm/p/11493026.html

时间: 2024-10-02 02:56:31

CF449D Jzzhu and Numbers 高维前缀和的相关文章

CF449D Jzzhu and Numbers (状压DP+容斥)

题目大意: 给出一个长度为n的序列,构造出一个序列使得它们的位与和为0,求方案数 也就是从序列里面选出一个非空子集使这些数按位与起来为0. 看了好久才明白题解在干嘛,我们先要表示出两两组合位与和为0的所有情况 先hx一下每个数出现的次数,然后我们从遍历 i ,i 是二进制的数位 然后遍历所有的情况,如果第 i 位有1,那么说明我们去掉第 i 位的1就是又一种情况! 其实我们统计的是所有数在删掉/不删掉每一位的1 所有可能出现的数! 那么,状态内任意组合,不能取空集,总数就是 再根据容斥原理(最玄

Codeforces 449D:Jzzhu and Numbers(高维前缀和)

[题目链接] http://codeforces.com/problemset/problem/449/D [题目大意] 给出一些数字,问其选出一些数字作or为0的方案数有多少 [题解] 题目等价于给出一些集合,问其交集为空集的方案数, 我们先求交集为S的方案数,记为dp[S],发现处理起来还是比较麻烦, 我们放缩一下条件,求出交集包含S的方案数,记为dp[S], 我们发现dp[S],是以其为子集的方案的高维前缀和, 我们逆序求高维前缀和即可,之后考虑容斥,求出交集为0的情况, 我们发现这个容斥

4.7-4.9补题+水题+高维前缀和

题目链接:51nod 1718 Cos的多项式  [数学] 题解: 2cosx=2cosx 2cos2x=(2cosx)^2-2 2cos3x=(2cosx)^3-3*(2cosx) 数归证明2cos(nx)能表示成关于2cosx的多项式,设为f(n) f(1)=x,f(2)=x^2-2(其中的x就是2cosx) 假设n=1~k时均成立(k>=3) 当n=k+1时 由cos((k+1)x)=cos(kx)cos(x)-sin(kx)sin(x) cos((k-1)x)=cos(kx)cos(x)

hihocoder1496(高维前缀和)

题意:给定N个数A1, A2, A3, ... AN,小Ho想从中找到两个数Ai和Aj(i ≠ j)使得乘积Ai × Aj × (Ai AND Aj)最大.其中AND是按位与操作. 第一行一个整数N(1<=N<=100,000) 第二行N个整数A1, A2, A3, ... AN (0 <= Ai <2^20) 分析: 尝试枚举and值z,那么问题就变成了找寻最大的x*y,使得x&y==z 把这个要求放宽一点,我们来寻找z是x&y子集的情况(这样肯定不会丢掉整体最优

Codeforces 449D:Jzzhu and Numbers

Codeforces 449D:Jzzhu and Numbers 题目链接:http://codeforces.com/problemset/status?friends=on 题目大意:给出$n$个数,求有多少种组合使得$a_{i_1}\&a_{i_2}\&...\&a_{i_k}=0(0 \leqslant i < n)$,答案对$10^9+7$取模. 容斥原理+DP 设位与$(\&)$后二进制表示中有$k$个$1$的组合数为$A_k$,则有, $A_0=$所有

SPOJ Time Limit Exceeded(高维前缀和)

[题目链接] http://www.spoj.com/problems/TLE/en/ [题目大意] 给出n个数字c,求非负整数序列a,满足a<2^m 并且有a[i]&a[i+1]=0,对于每个a[i],要保证a[i]不是c[i]的倍数, 求这样的a[i]序列的个数 [题解] 我们用dp[i]表示以i为结尾的方案数, 我们发现要满足a[i]&a[i+1]=0,则dp[i]是从上一次结果中所有满足i&j=0的地方转移过来的 i&j=0即i&(~j)=i,即i为~

codeforces 938F(dp+高维前缀和)

题意: 给一个长度为n的字符串,定义$k=\floor{log_2 n}$ 一共k轮操作,第i次操作要删除当前字符串恰好长度为$2^{i-1}$的子串 问最后剩余的字符串字典序最小是多少? 分析: 首先很容易得到一个性质,那就是删除的那些串是可以不交叉的 很容易想到一个很简单的dp dp[i][j]表示考虑原串的前i位,删除状态为j的情况下字典序最小的字符串(注意dp里面保存的是个字符串) 那么很明显就是个O(n^3logn)的dp,无法通过 dp里是一个字符串这个东西是很浪费时间而且很不优美的

[2018.6.21集训]走路-分块高维前缀和-Pollard-Rho

题目大意 给一棵树,每个节点有一个权值$val$. 如果两个点$a$和$b$满足$a$为$b$的祖先且$val[b]$为$val[a]$的约数,那么可以从$a$一步跳到$b$. 求从$1$号节点走到各每个节点的路径数. $n \leq 10^5 , val[i] \leq 10^{18},$保证对于任意节点$i$,$val[i]$为$val[1]$的约数. 题解 首先有定理:一个数$n$的约数个数大概是$n^{\frac{1}{3}}$的级别 然后得到有理有据的一个部分分($val[i] \le

高维前缀和

我们经常要用到前缀和. 一维: for(int i=1;i<=n;i++) b[i]=b[i-1]+a[i]; 二维: for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j]; 那如果是三维的呢? for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=p;k++) b[i][j][