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;
}
posted @ 2018-11-06 22:10  Garen-Wang  阅读(149)  评论(0编辑  收藏  举报