【NOI2017】 整数(二进制计数器)

题面

题目背景

在人类智慧的山巅,有着一台字长为 1   048   576 1\,048\,576 1048576 位(此数字与解题无关)的超级计算机,著名理论计算机科学家 P 博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,超级计算机无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你…

题目描述

P 博士将他的计算任务抽象为对一个整数的操作。

具体来说,有一个整数 x x x,一开始为 0 0 0

接下来有 n n n 个操作,每个操作都是以下两种类型中的一种:

  • 1 a b:将 x x x 加上整数 a ⋅ 2 b a\cdot 2^b a2b,其中 a a a 为一个整数, b b b 为一个非负整数

  • 2 k :询问 x x x 在用二进制表示时,位权为 2 k 2^k 2k 的位的值(即这一位上的 1 1 1 代表 2 k 2^k 2k

保证在任何时候, x ⩾ 0 x\geqslant 0 x0

输入格式

输入的第一行包含四个正整数 n , t 1 , t 2 , t 3 n,t_1,t_2,t_3 n,t1,t2,t3 n n n 的含义见题目描述, t 1 t_1 t1 t 2 t_2 t2 t 3 t_3 t3 的具体含义见子任务。

接下来 n n n 行,每行给出一个操作,具体格式和含义见题目描述。

输出格式

对于每个询问操作,输出一行,表示该询问的答案( 0 0 0 1 1 1)。对于加法操作,没有任何输出。

样例 #1

样例输入 #1

10 3 1 2
1 100 0
1 2333 0
1 -233 0
2 5
2 7
2 15
1 5 15
2 15
1 -1 12
2 15

样例输出 #1

0
1
0
1
0

提示

在所有测试点中, 1 ⩽ t 1 ⩽ 3 , 1 ⩽ t 2 ⩽ 4 , 1 ⩽ t 3 ⩽ 2 1\leqslant t_1 \leqslant 3, 1 \leqslant t_2 \leqslant 4, 1 \leqslant t_3 \leqslant 2 1t13,1t24,1t32。不同的 t 1 , t 2 , t 3 t_1, t_2, t_3 t1,t2,t3 对应的特殊限制如下:

  • 对于 t 1 = 1 t_1 = 1 t1=1 的测试点,满足 a = 1 a = 1 a=1
  • 对于 t 1 = 2 t_1 = 2 t1=2 的测试点,满足 ∣ a ∣ = 1 |a| = 1 a=1
  • 对于 t 1 = 3 t_1 = 3 t1=3 的测试点,满足 ∣ a ∣ ⩽ 1 0 9 |a| \leqslant 10^9 a109
  • 对于 t 2 = 1 t_2 = 1 t2=1 的测试点,满足 0 ⩽ b , k ⩽ 30 0 \leqslant b, k \leqslant 30 0b,k30
  • 对于 t 2 = 2 t_2 = 2 t2=2 的测试点,满足 0 ⩽ b , k ⩽ 100 0 \leqslant b, k \leqslant 100 0b,k100
  • 对于 t 2 = 3 t_2 = 3 t2=3 的测试点,满足 0 ⩽ b , k ⩽ n 0 \leqslant b, k \leqslant n 0b,kn
  • 对于 t 2 = 4 t_2 = 4 t2=4 的测试点,满足 0 ⩽ b , k ⩽ 30 n 0 \leqslant b, k \leqslant 30n 0b,k30n
  • 对于 t 3 = 1 t_3 = 1 t3=1 的测试点,保证所有询问操作都在所有修改操作之后;
  • 对于 t 3 = 2 t_3 = 2 t3=2 的测试点,不保证询问操作和修改操作的先后顺序。

本题共 25 个测试点,每个测试点 4 分。各个测试点的数据范围如下:

测试点编号 n ≤ n \le n t 1 t_1 t1 t 2 t_2 t2 t 3 t_3 t3
1 1 1 10 10 10 3 3 3 1 1 1 2 2 2
2 2 2 100 100 100 3 3 3 2 2 2 2 2 2
3 3 3 2000 2000 2000 3 3 3 2 2 2 2 2 2
4 4 4 4000 4000 4000 1 1 1 3 3 3 2 2 2
5 5 5 6000 6000 6000 3 3 3 3 3 3 1 1 1
6 6 6 8000 8000 8000 2 2 2 3 3 3 2 2 2
7 7 7 9000 9000 9000 3 3 3 4 4 4 2 2 2
8 8 8 10000 10000 10000 3 3 3 3 3 3 2 2 2
9 9 9 30000 30000 30000 3 3 3 4 4 4 2 2 2
10 10 10 50000 50000 50000 3 3 3 4 4 4 1 1 1
11 11 11 60000 60000 60000 3 3 3 3 3 3 2 2 2
12 12 12 65000 65000 65000 2 2 2 4 4 4 2 2 2
13 13 13 70000 70000 70000 3 3 3 4 4 4 2 2 2
14 14 14 200000 200000 200000 3 3 3 4 4 4 2 2 2
15 15 15 300000 300000 300000 2 2 2 4 4 4 2 2 2
16 16 16 400000 400000 400000 3 3 3 4 4 4 2 2 2
17 17 17 500000 500000 500000 3 3 3 3 3 3 2 2 2
18 18 18 600000 600000 600000 3 3 3 4 4 4 2 2 2
19 19 19 700000 700000 700000 3 3 3 4 4 4 2 2 2
20 20 20 800000 800000 800000 1 1 1 4 4 4 2 2 2
21 21 21 900000 900000 900000 2 2 2 4 4 4 2 2 2
22 22 22 930000 930000 930000 3 3 3 3 3 3 2 2 2
23 23 23 960000 960000 960000 3 3 3 4 4 4 1 1 1
24 24 24 990000 990000 990000 3 3 3 3 3 3 2 2 2
25 25 25 1000000 1000000 1000000 3 3 3 4 4 4 2 2 2

题解

写这篇博客的目的就是记下这个结论:
https://www.luogu.com.cn/blog/_post/38789
在这里插入图片描述
知道了这个结论后,我们用手写bitset模拟加法。同时进行减法的话复杂度得不到保证,因此我们分别维护正数之和与负数之和。

设正数和为 A A A ,负数和为 B B B(取绝对值),当前要查的位为 i i i ,那么答案就是 A [ i ] ⊕ B [ i ] ⊕ ( A [ i − 1...1 ] < B [ i − 1...1 ] ) A[i]\oplus B[i]\oplus\big(A[i-1...1]<B[i-1...1]\big) A[i]B[i](A[i1...1]<B[i1...1]) (取模 2 i + 1 2^{i+1} 2i+1 的加减法)。

判断两个等长的数的大小关系,可以查找第一个不同的位置。我们同时维护bitset(手写)每个块是否二者相等,不相等就把块编号放进 s e t set set ,在 s e t set set 中查找。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

CODE

代码真的很简单

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
// #pragma GCC optimize(2)
using namespace std;
#define MAXN 1000005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
const ULL nm32 = (1ll<<32)-1;
ULL bt1[MAXN],bt2[MAXN];
set<int> st;
void addb1(ULL x,int p) { // plus
	if(!x) return ;
	x <<= (p&31); p >>= 5;
	bt1[p] += x;
	x = bt1[p] >> 32; bt1[p] &= nm32;
	if(bt1[p] != bt2[p]) st.insert(p);
	else st.erase(p);
	return addb1(x,(p+1)<<5);
}
void addb2(ULL x,int p) { // minus
	if(!x) return ;
	x <<= (p&31); p >>= 5;
	bt2[p] += x;
	x = bt2[p] >> 32; bt2[p] &= nm32;
	if(bt2[p] != bt1[p]) st.insert(p);
	else st.erase(p);
	return addb2(x,(p+1)<<5);
}
int main() {
	n = read();read();read();read();
	for(int i = 1;i <= n;i ++) {
		k = read();
		if(k == 1) {
			s = read(); o = read();
			if(s>=0) addb1(s,o);
			else addb2(-s,o);
		}
		else {
			s = read();
			o = s>>5; s &= 31;
			int me = ((bt1[o]>>s)^(bt2[o]>>s))&1;
			ULL nm = (1ll<<s)-1;
			if((bt1[o] & nm) != (bt2[o] & nm)) {
				ULL as = (bt1[o] & nm) - (bt2[o] & nm);
				AIput(((as>>s)&1)^me,'\n'); continue;
			}
			auto j = st.lower_bound(o);
			if(j == st.begin()) {
				AIput(me,'\n'); continue;
			}
			j --;
			if(bt1[*j] < bt2[*j]) me ^= 1;
			AIput(me,'\n');
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值