BZOJ 2568 比特集合

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2568

题意:维护一个集合S,支持以下操作:

(1)INS M : 将元素 M 插入到集合S中;
(2)DEL M : 将集合S中所有等于 M 的元素删除;
(3)ADD M : 将集合S中的所有元素都增加数值M ;
(4)QBIT k : 查询集合中有多少个元素满足其二进制的第 k位为 1 。

思路:我看的这个

https://github.com/strongoier/OJ/tree/master/BZOJ/2568%20%E6%AF%94%E7%89%B9%E9%9B%86%E5%90%88

原文的大致思路如下:

(1) ADD 操作的那个和单独拿出来,设为sum,集合S中的每个元素x实际值为x+sum;

(2)设f[k][t]表示第k位为1,且这个数字小于等于d的数字的个数。每次查询时,设L=2^x,R=2^(x+1)-1,则答案为f[k][R]-f[k][L-1];

(3)由于增加一个数字时这个f值是成段改变,因此要用树状数组维护这个f数组;

(4)对于那些插入的数字都是多少以及每个数字有多少个,用一个map记录,这样删除时就知道在树状数组要减去多少。

我当时有个问题没有明白,因为插入x时实际要插入的数字是x-sum,那么x-sum为负数时这个位跟正数的位不太一样。负数的二进制表示是对应正数的二进制表示取反加1。

比如

-1=11111111 11111111  11111111 11111111

-2=11111111 11111111  11111111 11111110

-3=11111111 11111111  11111111 11111101

-4=11111111 11111111  11111111 11111100

上面那个大神的插入是直接插入

后面那还加了个1是因为树状数组里下标都是从1开始的。

然后求和时是这样的

这个分为两部分,第一部分:计算的是[L,R]区间,设k=2,那么二进制表示L=100,R=111。设sum=1011,那么实际要计算的区间为[001,100],只要一个数字的后三位在这个区间,即[001,100],那么它加上sum之后的后三位都会落到[L,R]区间。其实这个是没有进位的。

我们再设sum=1110,其他不变,那么上面的实际求和区间变成[000,001]。我们发现,除了这个区间,[110,111]这个区间也是可以的。这个其实是进位产生的,进位之后求和区间由[100,111]变为[1100,1111],这样减去sum的后三位110实际区间为[110,1001],我们发现1001,1000都不会有这个值,所以实际就是[110,111]。这就是上面求和的第二部分。

那么一个负数加上sum之后也可能到达这个区间,sum=1110,[-10,-7],这些负数的二进制为

-10=11111111 11111111  11111111 11110110

-9  =11111111 11111111  11111111 11110111

-8  =11111111 11111111  11111111 11111000

-7  =11111111 11111111  11111111 11111001

我们发现,后三位都在计算的两个区间里。所以负数不需要额外考虑。

int S[20][N];
map<int,int> mp;

int n;

void add(int k,int x,int t)
{
    while(x<N) S[k][x]+=t,x+=x&-x;
}

int get(int k,int x)
{
    int ans=0;
    while(x) ans+=S[k][x],x-=x&-x;
    return ans;
}

int main()
{

    n=myInt();

    int sum=0;
    while(n--)
    {
        char op[10];
        int x;
        scanf("%s%d",op,&x);
        if(‘A‘==op[0]) sum+=x;
        else if(‘I‘==op[0])
        {
            x-=sum;
            mp[x]++;
            for(int i=0;i<16;i++) add(i,(x&((1<<(i+1))-1))+1,1);
        }
        else if(‘D‘==op[0])
        {
            x-=sum;
            int t=mp[x];
            mp[x]=0;
            for(int i=0;i<16;i++) add(i,(x&((1<<(i+1))-1))+1,-t);
        }
        else if(‘Q‘==op[0])
        {
            int ans=0;
            int L=1<<x,R=(1<<(x+1))-1;
            ans+=get(x,min(1<<16,max(0,R-(sum&((1<<(x+1))-1))+1)));
            ans-=get(x,min(1<<16,max(0,L-(sum&((1<<(x+1))-1)))));
            L|=1<<(x+1);
            R|=1<<(x+1);
            ans+=get(x,min(1<<16,max(0,R-(sum&((1<<(x+1))-1))+1)));
            ans-=get(x,min(1<<16,max(0,L-(sum&((1<<(x+1))-1)))));
            printf("%d\n",ans);
        }
    }
}

  

时间: 2024-10-05 05:31:23

BZOJ 2568 比特集合的相关文章

[BZOJ2568]比特集合

这道题的思路还是比较好想的喵~ 首先令数组 C[k][num] 表示 2 进制最后 k 位 <=num 的数的个数 查询第 k 位为 1 即询问 C[k][(1<<k+1)-1]-C[k][(1<<k)-1] 的值 因为要支持插入.删除,C 数组应使用树状数组或线段树来维护 然后可以用类似于标记永久化的方法,把 ADD 操作搞掉喵~ 但是细节部分处理起来就比较麻烦了,WA 了好多次莫名其妙的 A 了 我也不敢写太多…… 1 #include <cstdio> 2

bzoj 2734: [HNOI2012]集合选数

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 779  Solved: 456[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,...,

bzoj 2734: [HNOI2012]集合选数 状压DP

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 560  Solved: 321[Submit][Status] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何

bzoj:4762: 最小集合

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4762 mark一下,有空要好好弄懂 #include<cstdio> #include<algorithm> using namespace std; int read_p,read_ca; inline int read(){ read_p=0;read_ca=getchar(); while(read_ca<'0'||read_ca>'9') read_ca

BZOJ 上帝与集合的正确用法 欧拉定理

题意:链接 方法:欧拉定理 解析: 首先你需要知道一个公式. 注意适用条件x>=Phi(C) 然而对于本道题来说x是无穷,所以可以直接上降幂公式解决. 证明 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 3000010 #define M 10000010 using namespace std; typedef l

【bzoj 2839】集合计数

权限题 根据广义容斥的套路就很好做了 设\(g_i\)表示交集至少有\(i\)个元素,\(f_i\)表示交集恰好有\(i\)个元素 显然有 \[g_i=\sum_{j=i}^n\binom{j}{i}f_j\] 二项式反演可得 \[f_i=\sum_{j=i}^n(-1)^{j-i}\binom{j}{i}g_j\] 我们求得就是\(f_k\) 我们考虑\(g\)如何求 我们先从\(n\)个元素里选择\(j\)个元素作为我们的交集,这里是\(\binom{n}{j}\),之后对于剩下的\(n-j

OpenGL和D3D11中的深度模版测试

    在OpenGL和D3D11的管线中,像素shader之后的操作就是深度模版测试,深度模版测试是以sample为单位进行的,就是一个像素上可以有多个采样点,每个采样点都有深度信息.深度模版测试对每个采样点都要进行一次,如果是msaa,最后要对每次采样的像素结果进行resolve,得到最终的结果.在下面的链接中有msaa的介绍. http://www.cnblogs.com/mikewolf2002/archive/2012/11/22/2783235.html     深度模版测试的流程如

系统分析师笔记--面向对象方法学

面向对象方法学 面向对象测试: 算法层:测试单个方法(成员函数).方法:等价类划分.组和功能测试.递归函数测试和多态消息测试. 类层:测试单个对象类.不变式边界测试.模态类测试.非模态类测试. 模板层:测试对象集成(一组协调工作的类的相互作用).多态服务测试和展平测试. 系统层:测试整个面向对象的系统. UML事物(元素) 1,结构事物.类.接口.协作.用例.活动类.构件.节点. 2,动作事物.交互.状态机. 3,分组事物.包. 4,注释事物. UML2.0 14种图: 1,类图.描叙一组类.接

UML建模快速入门02 UML介绍

2015/03/21 - 16:12 [声明]欢迎转载,但请保留文章原始出处:http://blog.csdn.net/yelangjueqi/article/details/44724765 1,UML概述 1.1,统一建模语言(Unified Modeling Language,UML)是一种绘制软件蓝图的标准语言.可以用uml对软件密集型系统的制品进行可视化.详述.构造和文档化. 1.2,要学习uml,一个有效的出发点是形成该语言的概念模型,这要求学习三个要素:uml的基本构造块, 支配这