P4991 愤怒的XiaoX
二进制操作+线段树永久化lazy操作
暴力模拟你可能都会写错哦!因为你二进制操作都可能写错!
让我们先解决这两个所谓的“基本操作”:
前\(k\)位按位取反
我们操作的思路是先把前\(k\)位独立分开,然后就对剩下的那\(k\)位进行单独操作。
如何对一个数取反?用~符号?
注意到一个二进制的结论:负数=正数的反码+1
所以我们可以先把这个数取相反数再减一,得到的就是反码了。
但是符号位可能会出锅,我们使用unsigned类型保险。
最后两部分加起来就是答案。
前\(k\)位按位翻转
同样还是那个思路,分离前\(k\)位出来。
如何对那\(k\)位操作?
直接把剩下的部分再复制一遍,然后按照相反的权重开始相乘,最后得到的就是翻转后的数字了。
得到翻转后的再跟前面的后\(k\)位相加就搞定了。
之后你就可以暴力辣
既然想出了正确的位运算操作,那顺便想正解了吧?
线段树标记永久化操作
这种线段树是奇怪的一种线段树,她不下传懒标记是最骚的。
这种线段树支持一些奇怪的下传标记,并且查询的时候你还要自带一个参数作为最终的标记。
实现起来思路并不是很难。因为你大部分时间是在看前面的位运算到底写没写对
代码:
#include<cstdio>
const int maxn = 50005;
int n, t, q, k;
int a[maxn];
int val[maxn << 2], lazy1[maxn << 2], lazy2[maxn << 2];
#define lson (root << 1)
#define rson (root << 1 | 1)
int qf(int x)
{
int mo = (1 << k);
int temp1 = x % mo;
x -= temp1;
unsigned int temp = -temp1 - 1;
temp %= mo;
return x + temp;
}
int fz(int x)
{
int mo = (1 << k);
int temp1 = x % mo;
x -= temp1;
int temp = 0;
for(int i = 0; i < k; i++)
{
temp += (1 << (k - i - 1)) * (temp1 & 1);
temp1 >>= 1;
}
return x + temp;
}
void add(int root, int l, int r, int pos, int v)
{
if(l == r)
{
val[root] = v;
}
else
{
int mid = (l + r) >> 1;
if(pos <= mid) add(lson, l, mid, pos, v);
else add(rson, mid + 1, r, pos, v);
}
}
void pushdown(int root)
{
if(lazy1[root])
{
lazy1[lson] ^= 1;
lazy1[rson] ^= 1;
lazy1[root] = 0;
}
if(lazy2[root])
{
lazy2[lson] ^= 1;
lazy2[rson] ^= 1;
lazy2[root] = 0;
}
}
void update(int root, int l, int r, int x, int y, int kind)
{
if(r < x || y < l) return;
if(x <= l && r <= y)
{
if(kind == 1) lazy1[root] ^= 1;
else if(kind == 2) lazy2[root] ^= 1;
return;
}
int mid = (l + r) >> 1;
update(lson, l, mid, x, y, kind);
update(rson, mid + 1, r, x, y, kind);
}
int query(int root, int l, int r, int pos, int lazyone, int lazytwo)
{
if(l == r)
{
if(lazy1[root]) lazyone ^= 1;
if(lazy2[root]) lazytwo ^= 1;
int res = val[root];
if(lazyone) res = qf(res);
if(lazytwo) res = fz(res);
return res;
}
if(lazy1[root]) lazyone ^= 1;
if(lazy2[root]) lazytwo ^= 1;
int mid = (l + r) >> 1;
if(pos <= mid) return query(lson, l, mid, pos, lazyone, lazytwo);
else return query(rson, mid + 1, r, pos, lazyone, lazytwo);
}
void free(int root, int l, int r)
{
if(l == r)
{
if(lazy1[root]) val[root] = qf(val[root]);
if(lazy2[root]) val[root] = fz(val[root]);
lazy1[root] = lazy2[root] = 0;
return;
}
pushdown(root);
int mid = (l + r) >> 1;
free(lson, l, mid);
free(rson, mid + 1, r);
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0', ch = getchar();
return s * ans;
}
void test_fz()
{
int x;
while(scanf("%d", &x) == 1) printf("%d\n", fz(x));
}
void test_qf()
{
int x;
while(scanf("%d", &x) == 1) printf("%d\n", qf(x));
}
int main()
{
//k = read();
//test_fz();
//test_qf();
n = read(), t = read();
for(int i = 1; i <= n; i++)
{
a[i] = read();
add(1, 1, n, i, a[i]);
}
for(int i = 1; i <= t; i++)
{
if(i != 1) free(1, 1, n);
q = read(), k = read();
for(int j = 1; j <= q; j++)
{
int opt = read();
if(opt == 1 || opt == 2)
{
int l = read(), r = read();
update(1, 1, n, l, r, opt);
}
else if(opt == 3)
{
int p = read();
printf("%d\n", query(1, 1, n, p, 0, 0));
}
}
}
return 0;
}