题目大意:让你维护一个数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. vue中使用mui的extra icon问题

    1. 元素类名更改 2. mui下的fonts文件夹中添加mui-icons-extra.ttf文件 3. mui下的css文件中添加icons-extra.css文件 4. main.js中导入im ...

  2. Linux下常用函数-字符串函数

    inux下常用函数-字符串函数 atof(将字符串转换成浮点型数)  相关函数   atoi,atol,strtod,strtol,strtoul 表头文件   #include <stdlib ...

  3. pycharm 永久激活 序列码 破解版

    如今人工智能的概念相当火爆,很多想学习编程求得高薪岗位的同志纷纷学起了Python,自带的idle不够智能,推荐使用pycharm编辑运行Python程序. 然而小萌新在安装pycharm时才会意识到 ...

  4. python第三周:集合、函数、编码、文件

    1.集合: 集合的创建: list_1 = set([1,2,3,4,5]) list_2 = set([2,3,44,7,8]) 集合的特性:集合是无序的,集合可以去掉重复的元素 集合的操作:求交集 ...

  5. 邓_ php SESSION

    学会php session可以在很多地方使用,比如做一个后台登录的功能,要让程序记住用户的session,其实很简单,看了下面的文章你就明白了. PHP session用法其实很简单它可以把用户提交的 ...

  6. swiper.js在隐藏/显示切换时,轮播出现bug的解决办法

    swiper在 swiper-container正常状态下显示,轮播是没有问题,但是当 swiper-container由隐藏切换至显示时(比如做图片查看时)会出现滑动bug,滑动卡顿而且最后一张可以 ...

  7. poj 1611 简单并查集的应用

    #include<stdio.h> #define N 31000 int pre[N]; int find(int x) { if(x!=pre[x])     pre[x]=find( ...

  8. 工具-Telerik trial安装图解

  9. 洛谷——P2440 木材加工

    https://www.luogu.org/problem/show?pid=2440#sub 题目背景 要保护环境 题目描述 题目描述: 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木 ...

  10. POJ 1950

    直接DFS,因为实在没想到什么剪枝了... 注意一点是,10.11使用的是1011哦 #include <iostream> #include <cstdio> #includ ...