Luogu 3822 [NOI2017]整数
看懂了的大佬的题解。(这个id太巨了,找不到他的blog)
考虑直接暴力算进位均摊复杂度是对的,证明戳这里。
但是题目要求我们支持一个减操作,这就相当于返回之前操作前的结果,这对于这种均摊的复杂度的东西来说简直是不可能的,分分钟$T$飞。
解决方法也很简单:对加减分别维护一个绝对值,询问的时候相减就好了,这样复杂度也是对的。
然后考虑询问:因为询问的时候要比较两个绝对值的大小,考虑一下向前面借位的情况, 然后就相当于找一找第$b$位之后的为$1$的位哪个先,这个过程只要在暴力的时候维护一个$set$就可以解决了。
然后考虑压一下位,这时候就从dalao那里学到了很神奇的$unsigned int$刚好用这玩意压$32$位。
这个东西有一个好处,就是自动溢出取模,那么加法的时候只要维护一个$tag$,看一下原来的数加上之后是不是比原来小,就可以判断是否有进位了。
注意到修改和查询的时候其实可能有不完整的块,所以先分别处理一下。
时间复杂度$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef unsigned int uint; const int N = 1e6 + 5; int qn; uint inc[N], dec[N]; set <int> s; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9'|| ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } int main() { int op; read(qn), read(op), read(op), read(op); for(int a, b; qn--; ) { read(op); if(op == 1) { read(a), read(b); int p = (uint)b / 32, q = (uint)b % 32; if(a > 0) { uint add = (uint)a << q, tag = (uint)a >> (31 - q); tag >>= 1; uint old = inc[p]; inc[p] += add, tag += (old > inc[p]); if(inc[p] ^ dec[p]) s.insert(p); else if(s.count(p)) s.erase(p); for(++p; tag != 0; ++p) { old = inc[p], inc[p] += tag, tag = (old > inc[p]); if(inc[p] ^ dec[p]) s.insert(p); else if(s.count(p)) s.erase(p); } } else { a = -a; uint add = (uint)a << q, tag = (uint)a >> (31 - q); tag >>= 1; uint old = dec[p]; dec[p] += add, tag += (old > dec[p]); if(inc[p] ^ dec[p]) s.insert(p); else if(s.count(p)) s.erase(p); for(++p; tag != 0; ++p) { old = dec[p], dec[p] += tag, tag = (old > dec[p]); if(inc[p] ^ dec[p]) s.insert(p); else if(s.count(p)) s.erase(p); } } } else { read(b); int p = b / 32, q = b % 32, ans = (((inc[p] >> q) ^ (dec[p] >> q)) & 1); int v1 = inc[p] % (1 << q), v2 = dec[p] % (1 << q); if(v1 < v2) printf("%d\n", ans ^ 1); else if(v1 > v2 || s.empty() || p <= (*s.begin())) printf("%d\n", ans); else { set <int> :: iterator it = s.lower_bound(p); --it; if(inc[*it] > dec[*it]) printf("%d\n", ans); else printf("%d\n", ans ^ 1); } } } return 0; }