发现考试题的线段树都调不明白了,来做点线段树题吧)

CF240F TorCoder

题目大意

给定一个仅包含小写字母的字符串 $S$,有 $m$ 次修改,每次修改会将子串 $S[l..r]$ 中的所有字母重排成一个新的字符串,满足新字符串是回文串且字典序最小,如果无法满足就忽略本次修改,求 $m$ 次修改后的字符串。

题解

首先我们思考什么时候可以修改,什么时候不能修改。

发现回文串有一个性质:如果回文串长度为奇数,那么肯定有且仅有一个字母出现了奇数次,而如果回文串长度为偶数,肯定不会有字符出现奇数次。

如果满足上面的条件,那么一定能构造出一个回文串。

我们可以先考虑统计每一个字符的数量:发现字符集只有 $26$,所以我们可以直接暴力使用 $26$ 个某种数据结构统计一段区间内有多少个这个字符。

那么统计出来数量之后,如何构造?

我们分别来考虑偶数长度和奇数长度:

偶数长度比较简单,我们需要满足字典序最小,那么我们可以先填前一半,直接从 $a$ 开始填,一直填到 $z$,然后后一半直接对应的填过去,这样就可以满足字典序最小。

奇数长度的话,我们需要记录一下出现了奇数次的那一个字符在哪里。我们首先单独拿出来一个这个字符,放在最中间,然后剩下的还按照偶数长度的进行构造就可以。

发现这样构造正好也会使得同一个字符所在的位置是一个区间,所以我们直接进行区间修改即可。

回顾一下,我们需要一个数据结构,满足:

所以直接线段树维护就可以了。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m;
struct SegmentTree {
    struct Node {
        int sum, set;
    }t[MAXN << 2];
    #define lc (i << 1)
    #define rc (i << 1 | 1)
    void pushUp(int i) {
        t[i].sum = t[lc].sum + t[rc].sum;
    }
    void pushDown(int i, int l, int r) {
        if (t[i].set != -1) {
            int mid = (l + r) >> 1;
            t[lc].sum = (mid - l + 1) * t[i].set;
            t[rc].sum = (r - mid) * t[i].set;
            t[lc].set = t[i].set;
            t[rc].set = t[i].set;
            t[i].set = -1;
        }
    }
    void build(int i = 1, int l = 1, int r = n) {
        if (l == r) {
            t[i].set = -1;
            return;
        }
        int mid = (l + r) >> 1;
        build(lc, l, mid);
        build(rc, mid + 1, r);
        t[i].set = -1;
    }
    void set(int a, int b, int v, int i = 1, int l = 1, int r = n) {
        if (l > r) return;
        if (a <= l && r <= b) {
            t[i].set = v;
            t[i].sum = (r - l + 1) * v;
            return;
        }
        pushDown(i, l, r);
        int mid = (l + r) >> 1;
        if (a <= mid) set(a, b, v, lc, l, mid);
        if (b > mid) set(a, b, v, rc, mid + 1, r);
        pushUp(i);
    }
    int query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) {
            return t[i].sum;
        }
        pushDown(i, l, r);
        int mid = (l + r) >> 1;
        int ans = 0;
        if (a <= mid) ans += query(a, b, lc, l, mid);
        if (b > mid) ans += query(a, b, rc, mid + 1, r);
        return ans;
    }
}st[26];
char ch[MAXN];
int cnt[26];
int main() {
    scanf("%d%d%s", &n, &m, ch + 1);
    for (int i = 0; i < 26; i++) st[i].build();
    for (int i = 1; i <= n; i++) {
        st[ch[i] - 'a'].set(i, i, 1);
    }
    while (m--) {
        int l, r; scanf("%d%d", &l, &r);
        for (int i = 0; i < 26; i++) cnt[i] = st[i].query(l, r);
        int odd = -1;
        bool flag = false;
        for (int i = 0; i < 26; i++) {
            if (cnt[i] & 1) { // 出现奇数次
                if ((r - l + 1) & 1) { // 奇数回文串
                    if (odd != -1) { // 存在两个出现奇数次的字符,无解
                        flag = true;
                        break;
                    } else {
                        odd = i;
                    }
                } else { // 偶数回文串,无解
                    flag = true;
                    break;
                }
            }
        }
        if (flag) continue;
        int cc = 0;
        if (odd != -1) {
            st[odd].set(l, r, 0);
            int mid = (l + r) >> 1;
            st[odd].set(mid, mid, 1);
        }
        for (int i = 0; i < 26; i++) {
            cnt[i] /= 2;
            if (cnt[i] == 0) continue;
            if (i != odd) st[i].set(l, r, 0);
            st[i].set(l + cc, l + cc + cnt[i] - 1, 1);
            st[i].set(r - cc - cnt[i] + 1, r - cc, 1);
            cc += cnt[i];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 26; j++) {
            if (st[j].query(i, i) == 1) {
                printf("%c", 'a' + j);
                break;
            }
        }
    }
    return 0;
}

CF242E XOR on Segment

题目大意

给定一个数列 $a_i$,要求支持以下操作:

  1. 求 $\sum_{i=l}^ra_i$。
  2. 给 $[l,r]$ 区间里的所有数异或一个 $x$。

发现异或对于每一位的贡献都是互相独立的,所以我们直接按照二进制拆分成若干个数,然后问题就变为了:对一个区间力的所有数进行翻转,很容易可以使用线段树维护。

反正洛谷题解交不了那就少说点吧

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m, a[MAXN];
struct SegmentTree {
    struct Node {
        int sum;
        bool flip;
    }t[MAXN << 2];
    #define lc (i << 1)
    #define rc (i << 1 | 1)
    void pushUp(int i) {
        t[i].sum = t[lc].sum + t[rc].sum;
    }
    void pushDown(int i, int l, int r) {
        if (t[i].flip) {
            int mid = (l + r) >> 1;
            t[lc].flip ^= 1;
            t[rc].flip ^= 1;
            t[lc].sum = (mid - l + 1) - t[lc].sum;
            t[rc].sum = (r - mid) - t[rc].sum;
            t[i].flip = 0;
        }
    }
    void flip(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) {
            t[i].flip ^= 1;
            t[i].sum = (r - l + 1) - t[i].sum;
            return;
        }
        pushDown(i, l, r);
        int mid = (l + r) >> 1;
        if (a <= mid) flip(a, b, lc, l, mid);
        if (b > mid) flip(a, b, rc, mid + 1, r);
        pushUp(i);
    }
    int query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) {
            return t[i].sum;
        }
        pushDown(i, l, r);
        int mid = (l + r) >> 1;
        int ans = 0;
        if (a <= mid) ans += query(a, b, lc, l, mid);
        if (b > mid) ans += query(a, b, rc, mid + 1, r);
        return ans;
    }
} st[21];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= 20; j++) if (a[i] >> j & 1) {
            st[j].flip(i, i);
        }
    }
    scanf("%d", &m);
    while (m--) {
        int op; scanf("%d", &op);
        if (op == 1) {
            int l, r; scanf("%d%d", &l, &r);
            long long ans = 0;
            for (long long i = 0; i <= 20; i++) {
                ans += ((long long) st[i].query(l, r)) << i;
            }
            printf("%lld\n", ans);
        } else {
            int l, r, x; scanf("%d%d%d", &l, &r, &x);
            for (int i = 0; i <= 20; i++) if (x >> i & 1) {
                st[i].flip(l, r);
            }
        }
    }
    return 0;
}

不是这些题怎么都这么水

挑题中,实在懒得做了