Luogu P4883 mzf的考验

题目传送门

早已离我远去的六一儿童节……


维护区间翻转,很容易可以想到用\(Splay\)来维护

唯一的难点就是如何维护操作\(2\)
因为异或是对于每一个二进制位上的操作,而\(d \leq 2 ^ {20}\),所以对于节点\(x\),我们可以开一个数组a[i],表示\(x\)和它的子树中第\(i\)个二进制位上的\(1\)的总和

题目比较卡常,需要开\(o2\)优化

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls tree[p].s[0]
#define rs tree[p].s[1]
#define LL long long
using namespace std;
LL read() {
    LL k = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9')
        k = k * 10 + c - 48, c= getchar();
    return k;
}
int root, tot;
struct zzz {
    int s[2], num, fa;
    LL a[25], val, sum, tag2;
    bool tag;
}tree[100010];
void up(int p) {
    tree[p].sum = tree[ls].sum + tree[rs].sum + tree[p].val;
    tree[p].num = tree[ls].num + tree[rs].num + 1;
    for(int i = 0; i <= 20; ++i)
        tree[p].a[i] = tree[ls].a[i] + tree[rs].a[i] + ((tree[p].val >> i) & 1);
}
void Xor(int p, LL x) {
    LL k = 0;
    for(int i = 0; i <= 20; ++i) {
        if((x >> i) & 1) tree[p].a[i] = tree[p].num - tree[p].a[i];
        k += 1ll * (1 << i) * tree[p].a[i];
    }
    //cout << k << endl;
    tree[p].tag2 ^= x; tree[p].val ^= x; tree[p].sum = k;
}
void rev(int p) {
    if(!p) return ;
    tree[p].tag ^= 1;
    swap(ls, rs);
}
void down(int p) {
    if(tree[p].tag) {
        rev(ls), rev(rs);
        tree[p].tag = 0;
    }
    if(tree[p].tag2) {
        Xor(ls, tree[p].tag2);
        Xor(rs, tree[p].tag2);
        tree[p].tag2 = 0;
    }
}
void rotate(int p) {
    int f = tree[p].fa;
    int ff = tree[f].fa;
    bool k = tree[f].s[1] == p;

    tree[ff].s[tree[ff].s[1] == f] = p;
    tree[p].fa = ff;

    tree[f].s[k] = tree[p].s[k ^ 1];
    tree[tree[p].s[k ^ 1]].fa = f;

    tree[p].s[k ^ 1] = f;
    tree[f].fa = p;

    up(f), up(p);
}
void Splay(int p, int goal) {
    while(tree[p].fa != goal) {
        int f = tree[p].fa;
        int ff = tree[f].fa;
        if(ff != goal)
            (tree[f].s[0] == p) ^ (tree[ff].s[0] == f) ? rotate(p) : rotate(f);
        rotate(p);
    }
    if(goal == 0) root = p;
}
LL a[100010];
void build(int &p, int l, int r) {
    if(l > r) return ;
    int mid = (l + r) >> 1;
    p = mid;
    tree[p].val = a[p];
    build(ls, l, mid-1), build(rs, mid+1, r);
    if(ls) tree[ls].fa = p; if(rs) tree[rs].fa = p;
    up(p);
}
int k_th(int x) {
    int p = root;
    if(tree[p].num < x) return false;
    while(p) {
        down(p);
        if(x > tree[ls].num + 1)
            x -= tree[ls].num + 1, p = rs;
        else if(tree[ls].num >= x) p = ls;
        else return p;
    }
    return p;
}
int main() {
    int n = read(), m = read();
    for(int i = 1; i <= n; ++i) a[i+1] = read();
    build(root, 1, n+2);
    while(m--) {
        int opt = read(), l = read(), r = read();
        l = k_th(l); r = k_th(r+2);
        Splay(l, 0); Splay(r, root);
        if(opt == 1) {
            rev(tree[tree[root].s[1]].s[0]);
        }
        else if(opt == 2) {
            Xor(tree[tree[root].s[1]].s[0], read());
        }
        else if(opt == 3) {
            printf("%lld\n", tree[tree[tree[root].s[1]].s[0]].sum);
        }
    }


    return 0;
}

弱化版题目CF242E XOR on Segment

只有异或操作,无区间翻转,可只用线段树维护。当然,你把上面的代码直接改个if也能过

posted @ 2019-11-14 10:32  MorsLin  阅读(98)  评论(0编辑  收藏  举报