题解 CF240F TorCoder

题意

给定一个由 $\texttt{a}$ 至 $\texttt{z}$ 组成的字符串,支持:

  • 重构区间 $\left[l,r\right]$ 使其构成一个字典序最小的回文串。
  • 最后整体查询。

题解

让重构的回文串字典序最小,就要让字典序小的排到更接近 $l,r$ 的位置,所以字典序小的字母优先计算。

对于每个字母,开棵线段树,维护该字母在区间内出现的次数,每次重构操作就推平,按字典序从小到大填满整个区间,这个很容易实现。

构造时区间长度会影响判断无解。

  • 当区间长度为奇数时,至少有一类字母出现次数为奇数,把这类字母放一个到回文中心。如果出现次数为奇数的字母数大于 $1$,显然回文串构造不出来,无解。

  • 当区间长度为偶数时,若存在某类字母出现了奇数,显然就构造不出,且每次会出现至少 $2$ 类,所以可以和第一类一起判了。

按字典序从 $\texttt{a}$ 到 $\texttt{z}$ 依次推平重构即可满足构成字典序最小的字符串。

时间复杂度为 $O(n\log n)$,再乘个字符集的常数 $O(\Sigma)$,$\Sigma=26$。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, Sigma = 26;
int n, m;
char s[N];
int a[N];
struct SegmentTree {
    int tag, sum, l, r;
} t[Sigma][N << 2];
#define ls x << 1
#define rs x << 1 | 1
void pushup(int num, int x) { t[num][x].sum = t[num][ls].sum + t[num][rs].sum; }
void pushdown(int num, int x) {
    if(~t[num][x].tag) {
        t[num][ls].tag = t[num][rs].tag = t[num][x].tag;
        t[num][ls].sum = (t[num][ls].r - t[num][ls].l + 1) * t[num][ls].tag;
        t[num][rs].sum = (t[num][rs].r - t[num][rs].l + 1) * t[num][rs].tag;
        t[num][x].tag = -1;
    }
}
void build(int num, int x, int l, int r) {
    t[num][x].l = l, t[num][x].r = r, t[num][x].tag = -1;
    if(l == r) {
        t[num][x].sum = a[l] == num;
        return;
    }
    int mid = (l + r) >> 1;
    build(num, ls, l, mid);
    build(num, rs, mid + 1, r);
    pushup(num, x);
}
void update(int num, int x, int l, int r, int val) {
    if(l <= t[num][x].l && t[num][x].r <= r) {
        t[num][x].tag = val;
        t[num][x].sum = (t[num][x].r - t[num][x].l + 1) * val;
        return;
    }
    pushdown(num, x);
    if(l <= t[num][ls].r) update(num, ls, l, r, val);
    if(t[num][rs].l <= r) update(num, rs, l, r, val);
    pushup(num, x);
}
int query(int num, int x, int l, int r) {
    if(l <= t[num][x].l && t[num][x].r <= r)
        return t[num][x].sum;
    pushdown(num, x);
    int res = 0;
    if(l <= t[num][ls].r) res += query(num, ls, l, r);
    if(t[num][rs].l <= r) res += query(num, rs, l, r);
    return res;
}
int cnt[Sigma], num, tot, curl, curr;
bool check;
int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    scanf("%d%d%s", &n, &m, s + 1);
    for(int i = 1; i <= n; i++)
        a[i] = s[i] - 'a';
    for(int i = 0; i < Sigma; i++)
        build(i, 1, 1, n);
    while(m--) {
        int l, r;
        tot = num = 0;
        scanf("%d%d", &l, &r);
        curl = l, curr = r;
        check = false;
        for(int i = 0; i < Sigma; i++) {
            cnt[i] = query(i, 1, l, r);
            if(cnt[i] & 1)
                tot++, num = i;
            if(tot > 1) {
                check = true;
                break;
            }   
        }
        if(check) continue;
        for(int i = 0; i < Sigma; i++)
            update(i, 1, l, r, 0);
        if((r - l + 1) & 1)
            cnt[num]--;
        for(int i = 0; i < Sigma; i++)
            if(cnt[i]) {
                update(i, 1, curl, curl + (cnt[i] >> 1) - 1, 1);
                update(i, 1, curr - (cnt[i] >> 1) + 1, curr, 1);
                curl += (cnt[i] >> 1), curr -= (cnt[i] >> 1);
            }
        if((r - l + 1) & 1)
            update(num, 1, (l + r) >> 1, (l + r) >> 1, 1);
    }
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < Sigma; j++)
            if(query(j, 1, i, i)) {
                printf("%c", j + 'a');
                break;
            }
    return 0;
}
posted @ 2021-04-30 20:03  Terac  阅读(13)  评论(0)    收藏  举报  来源