牛客-XOR TREE

题目传送门

sol:假如把这题树上这个条件改成序列,那么对于操作2,求某区间$[l, r]$所有连续子序列的异或和。观察得出若$r - l + 1$是偶数,则答案是$[l, r]$区间的异或和。否则,答案是$[l, r]$区间内下标奇偶性与$l$不同的元素的异或和。那么可以用三颗线段树来分别维护奇数下标、偶数下标、所有下标的异或和。至于在树上完成操作,树剖解决。

  • 树剖 + 线段树
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 2e5 + 10;
    inline int read() {
        int n = 0; char c = getchar();
        while (c < '0' || c > '9') c = getchar();
        while (c >= '0' && c <= '9') {
            n = 10 * n + (c ^ '0');
            c = getchar();
        }
        return n;
    }
      
    struct {int v, n;} edge[2 * MAXN];
    int val[MAXN], head[MAXN];
    void add_edge(int u, int v) {
        static int cnt = 1;
        edge[cnt].v = v;
        edge[cnt].n = head[u];
        head[u] = cnt++;
    }
    // 树剖
    int fat[MAXN], son[MAXN], siz[MAXN], dep[MAXN];
    int top[MAXN], rnk[MAXN], ord[MAXN];
    void dfs(int u, int f, int d) {
        fat[u] = f, dep[u] = d, siz[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].n) {
            int v = edge[i].v;
            if (v == fat[u]) continue;
            dfs(v, u, d + 1);
            siz[u] += siz[v];
            if (siz[v] > siz[son[u]]) {
                son[u] = v;
            }
        }
    }
    void dfs(int u, int t) {
        static int cnt = 1;
        top[u] = t, ord[u] = cnt, rnk[cnt++] = u;
        if (!son[u]) return;
        dfs(son[u], t);
        for (int i = head[u]; i != -1; i = edge[i].n) {
            int v = edge[i].v;
            if (v == fat[u] || v == son[u]) continue;
            dfs(v, v);
        }
    }
      
    // 线段树
    struct SegTree {
        struct Node {
            int l, r;
            int val;
        } node[4 * MAXN];
        void build(int i, int l, int r) {
            node[i].l = l, node[i].r = r;
            node[i].val = 0;
            if (l == r) return;
            int mid = l + r >> 1;
            build(i << 1, l, mid);
            build(i << 1 | 1, mid + 1, r);
        }
        void update(int i, int ind, int val) {
            if (node[i].l == node[i].r) {
                node[i].val = val;
                return;
            }
            int mid = node[i].l + node[i].r >> 1;
            if (ind <= mid) update(i << 1, ind, val);
            else update(i << 1 | 1, ind, val);
            node[i].val = node[i << 1].val ^ node[i << 1 | 1].val;
        }
        int query(int i, int l, int r) {
            if (node[i].l == l && node[i].r == r) return node[i].val;
            int mid = node[i].l + node[i].r >> 1;
            if (r <= mid) return query(i << 1, l, r);
            if (l > mid) return query(i << 1 | 1, l, r);
            return query(i << 1, l, mid) ^ query(i << 1 | 1, mid + 1, r);
        }
    } tree[3];
    // 求路径和
    int query(int u, int v) {
        int val = 0, tag;
        int tu = top[u], tv = top[v];
        if ((dep[u] & 1) != (dep[v] & 1)) tag = 2;
        else tag = !(dep[u] & 1);
        while (tu != tv) {
            if (dep[tu] >= dep[tv]) { // 跳tu到u
                val ^= tree[tag].query(1, ord[tu], ord[u]);
                u = fat[tu], tu = top[u];
            } else {
                val ^= tree[tag].query(1, ord[tv], ord[v]);
                v = fat[tv], tv = top[v];
            }
        }
        if (dep[u] <= dep[v]) {
            val ^= tree[tag].query(1, ord[u], ord[v]);
        } else {
            val ^= tree[tag].query(1, ord[v], ord[u]);
        }
        return val;
    }
    // 主函数
    int main() {
        int n = read(), m = read();
        for (int i = 1; i <= n; i++) val[i] = read();
        memset(head, -1, sizeof(head));
        for (int i = 2; i <= n; i++) {
            int u = read(), v = read();
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs(1, -1, 1);
        dfs(1, 1);
        tree[0].build(1, 1, n), tree[1].build(1, 1, n), tree[2].build(1, 1, n);
        for (int i = 1; i <= n; i++) {
            tree[dep[i] & 1].update(1, ord[i], val[i]);
            tree[2].update(1, ord[i], val[i]);
        }
        for (int i = 1; i <= m; i++) {
            int op = read();
            if (op == 1) {
                int ind = read(), val = read();
                tree[dep[ind] & 1].update(1, ord[ind], val);
                tree[2].update(1, ord[ind], val);
            } else {
                int u = read(), v = read();
                printf("%d\n", query(u, v));
            }
        }
        return 0;
    }

    因为这道题专门去学了树剖,这题的数据真的是很玄学啊。我一样的代码第一次提交过80%数据超时,之后提交就能AC。另外一份感觉复杂度一样的代码一直过80%数据超时。最后20%数据有毒。

posted @ 2020-03-05 16:04  Jathon-cnblogs  阅读(249)  评论(0编辑  收藏  举报