[NOI2017] 整数

一、题目

点此看题

二、解法

首先要知道暴力为什么慢才能去优化,首先如果 \(a\) 只有正数的话那么修改的时间是均摊 \(O(n)\) 的,但是会有几部耗时很久,如果回撤的话就会非常耗时。这道题 \(a\) 是负数相当于回撤的效果,所以最坏情况是 \(O(n^2)\) 级别的。

由于只有加法是很快的,我们不妨把加法和减法分开维护,大概的思路是修改分别在加法数组和减法数组上面改,查询就用加法数组和减法数组减一下就行了。

因为 \(n\) 太大了所以还要用到压位的奇技淫巧,我们把 \(32\) 个字符压成一位,我们把压位后的这部分称为块,那么每次就找到对应的块修改,然后有进位暴力进上去即可,由于 \(a\leq 2^{30}\) 所以这部分时间复杂度 \(O(n)\)

然后就是查询了,这里要用到一个性质:加法数组的任意前缀一定比减法数组的对应前缀大,加入我们询问位置 \(k\)\(0\) 还是 \(1\),首先仅考虑这个位置看是 \(0\) 还是 \(1\)(也就是先把前缀做减法),再判断后缀的大小,如果加法数组的后缀更大那么无需进位,答案就是仅考虑前缀的答案,如果减法数组的后缀更大那么需要进位,答案需要异或 \(1\)

那么怎么比较两个数组后缀的大小关系呢?其实就像比字典序那样就行了,首先我们比较 \(k\) 所在块内那零散的一点点的大小关系,然后找到后面第一个不同的位置,比较大小关系即可,那么可以用 \(set\) 维护出所有不同的位置,然后直接 \(\tt lower\_bound\) 即可,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <set>
using namespace std;
#define ui unsigned int
const int M = 1000005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n;ui ad[M],sb[M];set<int> s;
signed main()
{
	n=read();read();read();read();
	while(n--)
	{
		int op=read();
		if(op==1)
		{
			int a=read(),b=read(),p=b/32,q=b%32;
			if(a>0)
			{
				ui d=(ui)a<<q,ic=(ui)a>>(31-q);ic>>=1;
				ui tmp=ad[p];ad[p]+=d;ic+=(tmp>ad[p]);
				if(ad[p]!=sb[p]) s.insert(p);else s.erase(p);
				while(ic)
				{
					p++;
					tmp=ad[p];ad[p]+=ic;ic=(tmp>ad[p]);
					if(ad[p]!=sb[p]) s.insert(p);else s.erase(p);
				}
			}
			else
			{
				a=-a;
				ui d=(ui)a<<q,ic=(ui)a>>(31-q);ic>>=1;
				ui tmp=sb[p];sb[p]+=d;ic+=(tmp>sb[p]);
				if(ad[p]!=sb[p]) s.insert(p);else s.erase(p);
				while(ic)
				{
					p++;
					tmp=sb[p];sb[p]+=ic;ic=(tmp>sb[p]);
					if(ad[p]!=sb[p]) s.insert(p);else s.erase(p);
				}
			}
		}
		else
		{
			int b=read(),p=b/32,q=b%32,ans=((ad[p]>>q)^(sb[p]>>q))&1;
			ui x=ad[p]%(1ll<<q),y=sb[p]%(1ll<<q);
			if(x<y) printf("%d\n",ans^1);
			else if(x>y || s.empty() || p<=(*s.begin())) printf("%d\n",ans);
			else
			{
				set<int>::iterator it=s.lower_bound(p);it--;
				if(ad[*it]>sb[*it]) printf("%d\n",ans);
				else printf("%d\n",ans^1);
			}
		}
	}
}
posted @ 2021-03-17 09:40  C202044zxy  阅读(64)  评论(0编辑  收藏  举报