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;
}