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]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...
随机推荐
- [luogu2585 ZJOI2006] 三色二叉树 (树形dp)
传送门 Description Input 输入文件名:TRO.IN 输入文件仅有一行,不超过500000个字符,表示一个二叉树序列. Output 输出文件名:TRO.OUT 输出文件也只有一行,包 ...
- Windows环境下制作Ubuntu的U盘启动工具
Windows用户想要尝试一下Ubuntu或其他Linux系统?最简单的方法就是使用VMware Workstation,只是虚拟机会真实占用内存,如果你的电脑内存8G及以上可以试试.内存偏小,或者觉 ...
- Jquery 根据HTML内容选择元素
选择所有包含 "is" 的 元素: $("p:contains(is)")
- buntu Rhythmbox解决中文乱码
Ubuntu Rhythmbox解决中文乱码 在这里介绍的是一个解决方法,修改变量. 在终端输入: gedit ~/.profile 在最后加入下面内容: exportGST_ID3_TAG_ENCO ...
- 简洁又快速地处理集合——Java8 Stream(上)
Java 8 发布至今也已经好几年过去,如今 Java 也已经向 11 迈去,但是 Java 8 作出的改变可以说是革命性的,影响足够深远,学习 Java 8 应该是 Java 开发者的必修课. 今天 ...
- redis 在 Linux 和 Windows 上的安装配置
最近需要在服务器上安装 redis,虽然只是一个小事情,但这个过程中也遇到了不少的问题,所以做一个总结,也希望能给到其他人一些帮助. 本文记录了 linux 系统和 windows 系统的 redis ...
- CF789C. Functions again
/* CF789C. Functions again http://codeforces.com/contest/789/problem/C 水题 题意:求数组中的连续和的最大值 */ #includ ...
- Springboot分布式锁实践(redis)
springboot2本地锁实践一文中提到用Guava Cache实现锁机制,但在集群中就行不通了,所以我们还一般要借助类似Redis.ZooKeeper 之类的中间件实现分布式锁,下面我们将利用自定 ...
- 菜鸟的mongoDB学习---(六)MongoDB 索引
MongoDB 索引 ps:大概有半个月木有更新了,因为前一阶段的出差和这几天突然来的项目.导致上网时间急剧降低,实在是sorry,以后预计会好一点. 索引通常可以极大的提高查询的效率.假设没有索引. ...
- 深入分析JavaWeb Item39 -- 监听器(Listener)学习进阶
一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信 ...