把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3822】[NOI2017] 整数(压位+set)

点此看题面

大致题意: 给定一个整数\(x\)(初始为\(0\)),支持两种操作:给\(x\)加上\(a\times 2^b\);询问\(x\)二进制下第\(k\)位的值。

压位

遇到这种题应该自然而然就会想到压位吧。

一开始我以为\(a\)只有正数(日常眼瞎),那么实际上每次只要暴力加、暴力进位就可以了。

这种做法的复杂度我能口胡它是均摊\(O(1)\)的,但由于我的证明方式比较难以描述,无法用书面文字表达,因此略了。

然而,由于\(a\)可能存在负值,那么只要开局加一个\(2^{3\times 10^7}\),然后重复执行\(-1,+1,-1,+1,...\),相当于每次都要跑满,直接升天。

想了好久都没想出来该怎么修改这个做法,最终只好默默点开了题解。。。

结果发现我的做法其实已经非常接近正解了。

实际上,防止出现上述情况的最好方式,就是分别记录加上的数的总和减去的数的总和,那么复杂度依然有保证。

接着考虑询问,我们先求出加与减的总和第\(k\)位的差值,然后只需判断\(0\sim k-1\)位是否存在减法过程中出现借位即可。

判断出现借位,一位一位枚举过去显然是不现实的。而一个较为优秀的方法,就是找到最高的不同位,比较两个总和中这一位的大小。

那么如何找到最高的不同位呢?

其实,我们可以开一个\(set\),然后只要把所有两个总和不同的一段压位值的编号扔入\(set\)中,每次使用\(lower\_bound\)即可找到最高的不同位。

这道题就这样做完了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define U unsigned int
using namespace std;
U a[N+5],b[N+5];set<int> S;
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		int f;char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=(x<<3)+(x<<1)+(c&15),D);x*=f;}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
I void Add(U *s,U *k,Con U& x,CI y)
{
	#define F5(x) (s[x]^k[x]?(S.insert(x),0):S.count(x)&&(S.erase(x),0))
	RI p=y>>5,q=y&31;U t=s[p];s[p]+=x<<q,F5(p);//先处理当前位
	U w=((x>>1)>>(31-q))+(s[p++]<t);W(w) t=s[p],s[p]+=w,F5(p),w=s[p++]<t;//进位
}
I int Qry(CI x)
{
	RI p=x>>5,q=x&31,t=((a[p]^b[p])>>q)&1;//先求出第k位的差值
	if((a[p]^b[p])&((1<<q)-1)) return t^((a[p]&((1<<q)-1))<(b[p]&((1<<q)-1)));//如果这一段压位值中已经存在不同
	set<int>::iterator it=S.lower_bound(p);if(it==S.begin()) return t;//lower_bound找出最高的不同位
	return --it,t^(a[*it]<b[*it]);//比较
}
int main()
{
	RI Qt,i,op,x,y;F.read(Qt,op,x,y);W(Qt--)
		F.read(op,x),op==1?F.read(y),x>0&&(Add(a,b,x,y),0),x<0&&(Add(b,a,-x,y),0)//修改
		:(putchar(48|Qry(x)),putchar('\n'));//询问
	return 0;
}
posted @ 2020-06-18 15:59  TheLostWeak  阅读(197)  评论(0编辑  收藏  举报