题目大意:让你维护一个数x(x位数<=3*1e7),要支持加/减a*2^b,以及查询x的第i位在二进制下是0还是1

作为一道noi的题,非常考验写代码综合能力,敲+调+借鉴神犇的代码 3个多小时才过...

思路并不难,题目里b<=30n暗示压位,每次压30位可过

先分析一下加法,加a*2^b相当于在第b-1位加a,如果进位了(即>),那就在下一位+1,如果下一位还进位了,那就再下一位+1......

暴力进位显然不可取,那么用线段树维护它,维护连续的几大位之内是否全都是1

a<=1e9<=2^30,所以它最多被拆成两大位压进线段树里

那么每一个大位的加法操作可以变成 (方便描述,下面的数字是从左往右读):

1.当前位+=val,拆分出的val<=2^30

2.如果进位了,就去线段树里找接下来连续是1的最大的位置,然后在它下一位+1 比如01011 11111 10110,就是在第三位+1(实际操作可以直接找它下一位)

3.把这两位之间(不包括两端)的所有的大位都改成,区间修改打标记

减法操作和加法非常类似,借位就是找最右边是

查询操作非常简单没什么好说的,别忘了先下推标记再查询...

最先我的代码写了1个多小时,维护的东西一大堆,感觉恶心得一匹而且巨难调

实在是没想到好的优化方法,就借鉴了神犇gxzlegend的一些神级操作(orzorz):

a组(any)维护它子节点的&值,e数组(exist)维护它子节点的|值,这个维护方法很神,比我原来想的简单得多,而且不需要乱七八糟的打标记

find_inf和find_zero这两个函数也非常简洁明了

以及感谢LOJ的数据顺便吐槽一句辣鸡bzoj

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define inf (1<<30)-1
#define il inline
#define ls rt<<1
#define rs rt<<1|1
#define N 1000100
using namespace std;
//re
int n,ma,t1,t2,t3;
int a[N<<],e[N<<],tag[N<<]; int gc()
{
int rett=,fh=;char p=getchar();
while(p<''||p>''){if(p=='-')fh=-;p=getchar();}
while(p>=''&&p<=''){rett=(rett<<)+(rett<<)+p-'';p=getchar();}
return rett*fh;
}
il void pushup(int rt){
e[rt]=e[rt<<]|e[rt<<|];
a[rt]=a[rt<<]&a[rt<<|];
}
il void pushdown(int rt){
if(tag[rt]==-) a[ls]=a[rs]=e[ls]=e[rs]=,tag[rt]=,tag[ls]=tag[rs]=-;
if(tag[rt]==) a[ls]=a[rs]=e[ls]=e[rs]=inf,tag[rt]=,tag[ls]=tag[rs]=;
}
int find_inf(int x,int l,int r,int rt)
{
if(a[rt]==inf) return -;
if(l==r) return l;
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid){
int pos=find_inf(x,l,mid,rt<<);
if(pos==-) return find_inf(x,mid+,r,rt<<|);
else return pos;
}
return find_inf(x,mid+,r,rt<<|);
}
int find_zero(int x,int l,int r,int rt)
{
if(!e[rt]) return -;
if(l==r) return l;
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid){
int pos=find_zero(x,l,mid,rt<<);
if(pos==-) return find_zero(x,mid+,r,rt<<|);
else return pos;
}
return find_zero(x,mid+,r,rt<<|);
}
int upd1(int x,int l,int r,int rt,int p)
{
if(l==r)
{
a[rt]+=p,e[rt]+=p;
if(a[rt]>inf){a[rt]-=(inf+);e[rt]-=(inf+);return ;}
if(a[rt]<){a[rt]+=(inf+);e[rt]+=(inf+);return -;}
return ;
}
pushdown(rt);
int mid=(l+r)>>,ans=;
if(x<=mid) ans=upd1(x,l,mid,rt<<,p);
else ans=upd1(x,mid+,r,rt<<|,p);
pushup(rt);return ans;
}
void upd2(int L,int R,int l,int r,int rt,int val)
{
if(L<=l&&r<=R){
tag[rt]=(val)?:-;
a[rt]=e[rt]=(val)?inf:;
return;}
pushdown(rt);
int mid=(l+r)>>;
if(L<=mid) upd2(L,R,l,mid,rt<<,val);
if(R>mid) upd2(L,R,mid+,r,rt<<|,val);
pushup(rt);
}
void add(int x,int y)
{
int p=upd1(x,,n,,y);
if(!p) return;
int pos=find_inf(x+,,n,);
if(pos->=x+) upd2(x+,pos-,,n,,);
upd1(pos,,n,,);
}
void del(int x,int y)
{
int p=upd1(x,,n,,-y);
if(!p) return;
int pos=find_zero(x+,,n,);
if(pos->=x+) upd2(x+,pos-,,n,,);
upd1(pos,,n,,-);
}
int query(int x,int l,int r,int rt,int p)
{
if(l==r) return ((<<p)&a[rt])?:;
int mid=(l+r)>>;
pushdown(rt);
if(x<=mid) return query(x,l,mid,rt<<,p);
else return query(x,mid+,r,rt<<|,p);
}
int main()
{
scanf("%d%d%d%d",&n,&t1,&t2,&t3);
int fl,x,y;
for(int i=;i<=n;i++)
{
fl=gc();
if(fl==){
x=gc(),y=gc();
if(x>=){
add(y/,(x&((<<(-y%))-))<<(y%));
add(y/+,x>>(-y%));
}else{ x=-x;
del(y/,(x&((<<(-y%))-))<<(y%));
del(y/+,x>>(-y%));
}
}else{
x=gc();
printf("%d\n",query(x/,,n,,x%));
}
}
return ;
}

BZOJ 4942 NOI2017 整数 (压位+线段树)的更多相关文章

  1. 【bzoj4942】[Noi2017]整数 压位+线段树

    题目描述 P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 $x$ ,一开始为0. 接下来有 $n$ 个操作,每个操作都是以下两种类型中的一种: 1 a b :将 $x$ 加上整数 ...

  2. LOJ 2302 「NOI2017」整数——压位线段树

    题目:https://loj.ac/problem/2302 压30位,a最多落在两个位置上,拆成两次操作. 该位置加了 a 之后,如果要进位或者借位,查询一下连续一段 0 / 1 ,修改掉,再在含有 ...

  3. 【NOI】2017 整数(BZOJ 4942,LOJ2302) 压位+线段树

    [题目]#2302. 「NOI2017」整数 [题意]有一个整数x,一开始为0.n次操作,加上a*2^b,或询问2^k位是0或1.\(n \leq 10^6,|a| \leq 10^9,0 \leq ...

  4. BZOJ.4942.[NOI2017]整数(分块)

    BZOJ 洛谷 UOJ 可能是退役之前最后一个BZOJ rank1了? 参考这里. 如果没有减法,对一个二进制数暴力进位,均摊复杂度是\(O(1)\)的(要进\(O(n)\)次位就至少需要\(O(n) ...

  5. bzoj 4942: [Noi2017]整数

    Description Solution 加法减法可以分开考虑,如果只有加法的话,直接暴力进位复杂度是对的 询问的时候就是把两个二进制数做差,判断第 \(k\) 位的取值 实际上我们只需要判断 \(1 ...

  6. Bzoj 3813 奇数国 题解 数论+线段树+状压

    3813: 奇数国 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 748  Solved: 425[Submit][Status][Discuss] ...

  7. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  8. BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)

    BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...

  9. bzoj 3489 A simple rmq problem - 线段树

    Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...

随机推荐

  1. PuTTY_0.67.0.0工具链接linux

    1.虚拟机设置 在网络适配器中选中桥接模式,勾选复制物理网络链接状态(p)选项.点击确认. 2.开启虚拟机,检查是否安装有ssh服务器 a.查看是否启动ssh服务器 ps -a | grep ssh ...

  2. VS2015使用C++编写DLL,并在C#环境中调用【转】

    说明:本文是本人在学习封装DLL时看到的,觉得说得很清楚,为了防止忘记,特意复制下来学习用,原文链接:https://blog.csdn.net/songyi160/article/details/5 ...

  3. js 现给数字加三位一逗号间隔的种方法

    方法一:(ie下有问题) <script type= "text/javascript"> var num_s = "1232134456.546 " ...

  4. 洛谷——P1968 美元汇率

    https://www.luogu.org/problem/show?pid=1968#sub 题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程 ...

  5. opencv3.2+opencv_contrib+cmake

    转自原文 opencv3.2+opencv_contrib+cmake 心得体会 初学OpenCV发现opencv3.2(下载链接在附录)是没有xfeatures2d等模块的.第三方库opencv_c ...

  6. [SharePoint2010开发入门经典]9创建更好的用户体验----silverlight

    本章概要: 1.了解Silverlight 2.理解为什么使用Silverlight 3.介绍如何集成SharePoint和Silverlight

  7. Spring进阶之路(10)-Advice简单介绍以及通过cglib生成AOP代理对象

    Advice简单介绍 1. Before:在目标方法运行之前运行织入.假设Before的处理中没有进行特殊的处理.那么目标方法终于会运行,可是假设想要阻止目标方法运行时.能够通过抛出一个异常来实现.B ...

  8. 路由器一键桥接Android实现

    開始之前,首先说一下什么叫一键桥接,所谓一键桥接,就是点击一下.就能够对所连接的路由器进行网络中继设置.以实现路由器能够通过已有的无线路由器进行网络訪问. 那么实现这个功能有几种方法呢?能够说有非常多 ...

  9. getColor()方法过时的替代方法

    Android SDK 升級到 23 之後,getResource.getColor(R.color.color_name) 過時 使用新加入的方法 ContextCompat.getColor(co ...

  10. Map (就一个json.jar)

    public static void main(String[] args) { List<Map<Integer, String>> m = new ArrayList< ...