BZOJ 4942 NOI2017 整数 (压位+线段树)
题目大意:让你维护一个数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 整数 (压位+线段树)的更多相关文章
- 【bzoj4942】[Noi2017]整数 压位+线段树
题目描述 P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 $x$ ,一开始为0. 接下来有 $n$ 个操作,每个操作都是以下两种类型中的一种: 1 a b :将 $x$ 加上整数 ...
- LOJ 2302 「NOI2017」整数——压位线段树
题目:https://loj.ac/problem/2302 压30位,a最多落在两个位置上,拆成两次操作. 该位置加了 a 之后,如果要进位或者借位,查询一下连续一段 0 / 1 ,修改掉,再在含有 ...
- 【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 ...
- BZOJ.4942.[NOI2017]整数(分块)
BZOJ 洛谷 UOJ 可能是退役之前最后一个BZOJ rank1了? 参考这里. 如果没有减法,对一个二进制数暴力进位,均摊复杂度是\(O(1)\)的(要进\(O(n)\)次位就至少需要\(O(n) ...
- bzoj 4942: [Noi2017]整数
Description Solution 加法减法可以分开考虑,如果只有加法的话,直接暴力进位复杂度是对的 询问的时候就是把两个二进制数做差,判断第 \(k\) 位的取值 实际上我们只需要判断 \(1 ...
- Bzoj 3813 奇数国 题解 数论+线段树+状压
3813: 奇数国 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 748 Solved: 425[Submit][Status][Discuss] ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)
BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...
- bzoj 3489 A simple rmq problem - 线段树
Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...
随机推荐
- [bzoj3743 Coci2015] Kamp(树形dp)
传送门 Description 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的点)要集中到一个点举行聚会. 聚会结束后需要一辆车从举行聚会的 ...
- gud-cloud
架构:计算层:三台机器,包含处理器,内存条,外加一个64GB的存储器,这个存储器安装exsi主机,然后创建cvm虚拟机,硬盘空间选择在之前安装exsi主机的存储器里面 存储层:三台机器内置多个硬盘8T ...
- Python-基础-day3
基础数据类型 1.什么是数据类型? 我们人类可以很容易的分清数字与字符的区别,但是计算机并不能呀,计算机虽然很强大,但从某种角度上看又很傻,除非你明确的告诉它,1是数字,“汉”是文字,否则它是分不清1 ...
- MyEclipse 2014 有用的几个快捷键
ctrl+h fileSearch ------------------------------------- MyEclipse 快捷键1(CTRL) ---------------------- ...
- FFT卷积相加模板
struct Complex { double r,i; Complex(double _r,double _i):r(_r),i(_i){} Complex(){} Complex operator ...
- H3C防火墙——回环流量问题(内网终端通过外网IP访问内部服务器)
http://www.bubuko.com/infodetail-1533703.html
- 工具-NuGet
1.添加下载后,会将文件添加到当前项目的引用和bin目录中 ORM是一种插件/组件,将对集合对象的操作映射为对关系型数据库的操作,这个映射是相互的 来自为知笔记(Wiz)
- 经验之谈—OAuth授权流程图
事实上我们在开发中,常常须要解决获得用户的一些特定的数据,比方:能够选择使用微博登陆.使用QQ登陆等等.然后我们间接的获得用户的头像.昵称等信息.这些都涉及到OAuth授权的内容 OAuth授权有这么 ...
- android 分享到QQ空间的全部操作
http://wiki.open.qq.com/wiki/mobile/SDK下载 <!-- QZone分享必须加上以下两个activity --> &l ...
- C-结构体应用(10)
结构体是用来定义多种类型的复合类型,在 C语言中与类的区别在于结构体注重的是数据而类除了数据还包含函数,第2点区别在于结构体所声明的成员默认是"public"点.而类的默认是pri ...