【bzoj4942】[Noi2017]整数 压位+线段树
题目描述
P 博士将他的计算任务抽象为对一个整数的操作。
具体来说,有一个整数 $x$ ,一开始为0。
接下来有 $n$ 个操作,每个操作都是以下两种类型中的一种:
1 a b :将 $x$ 加上整数 $a⋅2^b$ ,其中 $a$ 为一个整数,$b$ 为一个非负整数
2 k :询问 $x$ 在用二进制表示时,位权为 $2^k$ 的位的值(即这一位上的 $1$ 代表 $2^k$ )
保证在任何时候,$x≥0$。
输入
从标准输入读入数据。
输入的第一行包含四个正整数 $n,t_1,t_2,t_3$ ,$n$ 的含义见题目描述,$t_1,t_2,t_3$ 的具体含义见子任务。
接下来 $n$ 行,每行给出一个操作,具体格式和含义见题目描述。
同一行输入的相邻两个元素之间,用恰好一个空格隔开。
输出
输出到标准输出。
对于每个询问操作,输出一行,表示该询问的答案(0或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
样例输出
0
1
0
1
0
题解
压位+线段树
考场上想出了 $n\log^2n$ 的做法,然而没有想出压位就GG了。。。
先说 $n\log^2n$ 的做法:
由于 $|a|$ 只有 $10^9$ ,因此把 $a$ 分解成 $\sum 2^i$ 的形式,就变成了 $\log n$ 次加/减 $2^k$ 。
考虑加法,如果这一位是0则直接改为1,否则进位,相当于这一位变成0,下一位+1。最终结果就是找到这一位下面第一个为0的位,把该位+1,[当前位,该位)变成0。
减法同理,如果是1则直接该为0,否则退位,找到这一位下面第一个为1的为,把该位-1,[当前位,该位)变成1。
使用线段树维护区间是否全为0/1,单次加减的时间复杂度就是 $\log n$ ,$n$ 次 $\log n$ 次加减,时间复杂度 $O(n\log^2n)$ ,这样只有68分。
考虑优化:维护01信息过于浪费,考虑压位,维护每连续30位的数是什么。这样一次操作最多给两个位加减,加法就判断这一位加了以后是否超过 $2^{30}-1$ ,超过就找下面第一个不为 $2^{30}-1$ 的位,把该位+1,(当前位,该位)变成0,当前位直接变成进位后的结果。减法同理。
其中维护一段区间是否全 $0$ /全 $2^{30}-1$ 可以通过维护区间或和区间与来解决。
这样压位后时间复杂度就变为了 $O(n\log n)$
#include <cstdio> #include <cctype> #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 using namespace std; const int m = 1000010 , s = (1 << 30) - 1; int vo[m << 2] , va[m << 2] , tag[m << 2]; char pbuf[2000010] , *pp = pbuf; inline char nc() { static char buf[100000] , *p1 , *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int read() { int ret = 0 , f = 0; char ch = nc(); while(!isdigit(ch)) f |= (ch == '-') , ch = nc(); while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc(); return f ? -ret : ret; } inline void pushup(int x) { vo[x] = vo[x << 1] | vo[x << 1 | 1]; va[x] = va[x << 1] & va[x << 1 | 1]; } inline void pushdown(int x) { if(~tag[x]) { int l = x << 1 , r = x << 1 | 1; vo[l] = vo[r] = va[l] = va[r] = tag[l] = tag[r] = tag[x]; tag[x] = -1; } } inline void fix(int p , int v , int l , int r , int x) { if(l == r) { vo[x] += v , va[x] += v; return; } pushdown(x); int mid = (l + r) >> 1; if(p <= mid) fix(p , v , lson); else fix(p , v , rson); pushup(x); } inline void update(int b , int e , int v , int l , int r , int x) { if(b <= l && r <= e) { vo[x] = va[x] = tag[x] = v; return; } pushdown(x); int mid = (l + r) >> 1; if(b <= mid) update(b , e , v , lson); if(e > mid) update(b , e , v , rson); pushup(x); } inline int findzero(int p , int l , int r , int x) { if(!vo[x]) return -1; if(l == r) return l; pushdown(x); int mid = (l + r) >> 1; if(p <= mid) { int t = findzero(p , lson); return ~t ? t : findzero(p , rson); } else return findzero(p , rson); } inline int findinf(int p , int l , int r , int x) { if(va[x] == s) return -1; if(l == r) return l; pushdown(x); int mid = (l + r) >> 1; if(p <= mid) { int t = findinf(p , lson); return ~t ? t : findinf(p , rson); } else return findinf(p , rson); } inline int query(int p , int l , int r , int x) { if(l == r) return va[x]; pushdown(x); int mid = (l + r) >> 1; if(p <= mid) return query(p , lson); else return query(p , rson); } inline void add(int p , int a) { int t = query(p , 0 , m , 1); if(t + a <= s) fix(p , a , 0 , m , 1); else { fix(p , a - s - 1 , 0 , m , 1); int q = findinf(p + 1 , 0 , m , 1); if(q != p + 1) update(p + 1 , q - 1 , 0 , 0 , m , 1); fix(q , 1 , 0 , m , 1); } } inline void del(int p , int a) { int t = query(p , 0 , m , 1); if(t - a >= 0) fix(p , -a , 0 , m , 1); else { fix(p , s + 1 - a , 0 , m , 1); int q = findzero(p + 1 , 0 , m , 1); if(q != p + 1) update(p + 1 , q - 1 , s , 0 , m , 1); fix(q , -1 , 0 , m , 1); } } int main() { int n = read() , opt , x , y , p; read() , read() , read(); while(n -- ) { opt = read() , x = read(); if(opt == 1) { y = read() , p = y / 30; if(x > 0) { add(p , (x << (y - p * 30)) & s); add(p + 1 , x >> (30 - (y - p * 30))); } else if(x < 0) { x = -x; del(p , (x << (y - p * 30)) & s); del(p + 1 , x >> (30 - (y - p * 30))); } } else { p = x / 30; *pp ++ = (query(p , 0 , m , 1) & (1 << (x - p * 30)) ? 1 : 0) ^ '0' , *pp ++ = '\n'; } } fwrite(pbuf , 1 , pp - pbuf , stdout); return 0; }