CF 240F. TorCoder

TorCoder

题意

给出一个长度为 \(n\) 的字符串 \(s\) ,有 \(m\) 次询问,每次给出 \(l_i, r_i\) ,将 \([l_i, r_i]\) 的子串交换顺序使其成为字典序最小的回文子串,如果不能就跳过这次询问,求 \(m\) 次询问后,字符串 \(s\) 的字面值。

分析

首先将 \([l_i, r_i]\) 重组,我们需要知道每个字符出现的次数,也就是查询 \([l_i, r_i]\) 中某个字符的出现次数。

对于字典序最小,只需要按字符顺序顺次进行覆盖即可,也就是需要区间覆盖。

那么可以开26个线段树,分别表示每个字符的操作。

能够重组成回文子串,至多有一个字符出现奇数次,把这个奇数放在中间,对于偶数次的字符,我们切成两段放在区间两端即可。

Code

#include <iostream>

using namespace std;

#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define rep(i, x, y) for(int i = x; i <= y; i ++ )

const int N = 100010;

struct seg_tree
{
    int l, r;
    int sum, tag;
};

seg_tree t[26][N<<2];

int n, m;
char s[N];

void pushup (int tr, int p)
{
    t[tr][p].sum = t[tr][lc(p)].sum + t[tr][rc(p)].sum;
}

void pushdown (int tr, int p)
{
    if (t[tr][p].tag != -1)
    {
        t[tr][lc(p)].tag = t[tr][rc(p)].tag = t[tr][p].tag;
        t[tr][p].tag = -1;
        t[tr][lc(p)].sum = (t[tr][lc(p)].r - t[tr][lc(p)].l + 1) * t[tr][lc(p)].tag;
        t[tr][rc(p)].sum = (t[tr][rc(p)].r - t[tr][rc(p)].l + 1) * t[tr][rc(p)].tag;
    }
}

void build (int tr, int p, int l, int r)
{
    t[tr][p].l = l; t[tr][p].r = r;
    t[tr][p].sum = 0; t[tr][p].tag = -1;
    if (l == r)
    {
        t[tr][p].sum = (s[l] - ('a' + tr)) == 0;
        return ;
    }
    int mid = l + r >> 1;
    build (tr, lc(p), l, mid); build (tr, rc(p), mid + 1, r);
    pushup(tr, p);
}

void modify (int tr, int p, int l, int r, int val)
{
    if (l <= t[tr][p].l && r >= t[tr][p].r)
    {
        t[tr][p].sum = (t[tr][p].r - t[tr][p].l + 1) * val;
        t[tr][p].tag = val;
        return ;
    }
    pushdown(tr, p);
    int mid = t[tr][p].l + t[tr][p].r >> 1;
    if (l <= mid) modify(tr, lc(p), l, r, val);
    if (r > mid) modify(tr, rc(p), l, r, val);
    pushup(tr, p);
}

int query (int tr, int p, int l, int r)
{
    if (l <= t[tr][p].l && r >= t[tr][p].r) return t[tr][p].sum;
    pushdown(tr, p);
    int mid = t[tr][p].l + t[tr][p].r >> 1;
    int ans = 0;
    if (l <= mid) ans += query(tr, lc(p), l, r);
    if (r > mid) ans += query(tr, rc(p), l, r);
    return ans;
}

void solve ()
{
    cin >> n >> m >> (s + 1);
    rep(i, 0, 25) build(i, 1, 1, n);
    rep(i, 1, m)
    {
        int l, r; cin >> l >> r;
        int tim[26], odd = 0; // 记录每个字母在这个区域的次数和奇数数量
        int pos_odd = -1; // 哪个字符是奇数个
        rep(j, 0, 25) tim[j] = query(j, 1, l, r); // 找出每个字符的出现次数
        rep(j, 0, 25) if (tim[j] & 1) ++ odd, pos_odd = j;
        if (odd > 1) continue; // 如果不止一个字符是奇数次,那么一定不能构成回文串
        rep(j, 0, 25) modify(j, 1, l, r, 0); // 清除区间的字符
        if (odd) -- tim[pos_odd], modify(pos_odd, 1, l + r >> 1, l + r >> 1, 1);
        int nl = l, nr = r; // 剩下的没有被覆盖的区间的两端
        rep(j, 0, 25) if (tim[j])
        {
            modify(j, 1, nl, nl + tim[j] / 2 - 1, 1);
            nl += tim[j] / 2;
            modify(j, 1, nr - tim[j] / 2 + 1, nr, 1);
            nr -= tim[j] / 2;
        }
    }
    rep(i, 1, n) rep(j, 0, 25) if (query(j, 1, i, i))
    {
        cout << char (j + 'a');
        break;
    }
}

/* Storms make trees take deeper roots.*/
signed main () 
{
    #ifdef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    #endif
        solve();

    return 0;
}
posted @ 2021-11-20 13:42  Rainea  阅读(91)  评论(0)    收藏  举报