【线段树压位、压位+SET】BZOJ4942 [Noi2017]整数

题面在这里

好像线段树压位可以直接暴艹过去……

考虑只有加法,那么暴力加1均摊复杂度是\(O(1)\)

因为每次加法都只会产生1个1,而1只能在加法中产生

所以每次进位变成0的1都对应着一次加法,均摊复杂度\(O(1)\)

然后这个有减法,我们就分别存加操作和减操作的绝对值

\(a\)拆分成\(\sum 2^k\),于是修改直接压位处理是\(O(1)\)

每次询问两个数相减的二进制第k位

考虑两个串在\([0,k-1]\)的大小,显然 加串\(\ge\)减串 时,答案是第k位的异或值

否则是第k位的同或值

直接用set维护两个\(unsigned\ int\)串不同的位置

比大小就是lower_bound找到第一个不等的位置比较

然后就好了……代码贼短

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
typedef unsigned int uint;
inline char nc(){
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
inline int red(){
int res=0,f=1;char ch=nc();
while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=nc();}
while ('0'<=ch&&ch<='9') res=res*10+ch-48,ch=nc();
return res*f;
}

const int maxn=1000005;
int q;
uint a[2][maxn];
set<int> S;
void ist(int x,int d){
uint i=x/32,j=x%32;
while (1){
uint t=a[d][i];
a[d][i]+=(1<<j);
if (a[d][i]==a[d^1][i]) S.erase(i);else S.insert(i);
if (a[d][i]>t) return;
i++;j=0;
}
}
bool check(int k){
uint i=k/32,j=k%32;
if ((a[0][i]&(1<<j)-1)!=(a[1][i]&(1<<j)-1)) return (a[0][i]&(1<<j)-1)<(a[1][i]&(1<<j)-1);
set<int>::iterator it=S.lower_bound(i);
if (it==S.begin()) return 1; it--;
return a[0][*it]<a[1][*it];
}
int main(){
q=red();red(),red(),red();
while (q--)
if (red()==1){
int x=red(),k=red(),d=(x>0?1:(x=-x,0));
for (int i=0;i<=30;i++)
if (x&(1<<i)) ist(i+k,d);
}else{
int k=red(); uint i=k/32,j=k%32;
if (check(k)) putchar((((a[0][i]>>j)^(a[1][i]>>j))&1)+48);
else putchar((((a[0][i]>>j)^(a[1][i]>>j)^1)&1)+48);
putchar('\n');
}
return 0;
}