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]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...
随机推荐
- 洛谷P1739 表达式括号匹配
题目描述 假设一个表达式有英文字母(小写).运算符(+,-,*,/)和左右小(圆)括号构成,以"@"作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返 ...
- luogu 3768 简单的数学题 (莫比乌斯反演+杜教筛)
题目大意:略 洛谷传送门 杜教筛入门题? 以下都是常规套路的变形,不再过多解释 $\sum\limits_{i=1}^{N}\sum\limits_{j=1}^{N}ijgcd(i,j)$ $\sum ...
- android Build系统
http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/ android Build系统 超链接
- hdu 4079简单贪心
#include<stdio.h> #include<string.h> #define ll __int64 #define N 11000 struct node { in ...
- html5 过程解决问题收集
1.使用画布时报错误: Uncaught TypeError: Object [object Object] has no method 'getContext' . 解决方法: $(function ...
- LiquidCrystal库函数
主要资料来源: 极客工坊-知识库 (LiquidCrystal库地址:http://wiki.geek-workshop.com/doku.php?id=arduino:libraries:liqui ...
- [using_microsoft_infopath_2010]Chapter8 使用InfoPath表单web部件
本章概要: 1.配置web part 2.创建web part连接 3.创建表单参数 4.利用其它浏览器表单参数
- Python Study (01) 之 特殊方法
Python深入:特殊方法和多范式 Python是一切皆对象,意思就是python的天生就是个"纯面向对象语言"呀. 但是!!! Python还是一个多范式语言(multi-par ...
- Cocos2d-x3.0 从代码中获取cocostudio编辑的UI控件
依据名字查找控件 须要包括的头文件及名字空间: #include "cocostudio/CocoStudio.h" #include "ui/CocosGUI.h&qu ...
- [UVALive 6661 Equal Sum Sets] (dfs 或 dp)
题意: 求从不超过 N 的正整数其中选取 K 个不同的数字,组成和为 S 的方法数. 1 <= N <= 20 1 <= K<= 10 1 <= S <= 15 ...