题解:CF610E Alphabet Permutations
模拟赛妙妙题!
题意
最开始有一个原始字符串 \(S\),长为 \(n\),由前 \(k\) 个小写字母组成。
有 \(m\) 次操作。
-
将原序列 \([l, r]\) 赋值 \(c\)。
-
给定 \(t\),是前 \(k\) 个小写字母的排列。将 \(t\) 复制 \(x\) 遍,记为 \(t'\)。
找到最小的 \(x\) 使得,\(S\) 是 \(t'\) 的子序列。
思路
思考方向:暴力,转化问题,无询问,数据结构优化。
最朴素的方法肯定是模拟。
我甚至没看到 \(t\) 是排列。
首先是 暴力。
遍历 \(S\),同时维护 \(t'\),枚举到了 \(S_i\),在 \(t'\) 中对应 \(t'_j\)
若 \(S_{i + 1}\) 无法匹配,就将 t' += t。
再将 \(j\) 移到对应位置。
接下来是 转化问题 。
计 \(p_i\) 为 \(S_i\) 在 \(p\) 中对应的位置。
若 \(p_i >= p_{i + 1}\) 说明答案必然要加 \(1\)。
然后是 无询问 情况。
我们设 \(cnt_{{i},{j}}\) 为 \(S\) 中有多少相邻元素满足前一个是 \(i\),后一个是 \(j\)。
若在 \(t\),中有两个元素 \(t_i\),\(t_j\),且 \(i \le j\)。则答案会加上 \(cnt_{t_j, t_i}\)。
优化:
这个东西就是颜色段均摊。
可以去 珂朵莉树/颜色段均摊 - OI Wiki 看。
对于修改操作,我们每次 \([l,r]\) 的贡献(相邻的块,和块内的)减去,再加上新的贡献(区间两端和内部)。
代码,挺短的:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, k;
int a[N];
char t[N];
int cnt[25][25];
struct node{
int l, r; int v;
node(const int &ll, const int &rr, const int &vv) : l(ll), r(rr), v(vv) {}
bool operator < (const node &o) const{
return l < o.l;
}
};
set<node> s;
auto split(int x) {
if(x == n + 1) return s.end();
auto it = s.lower_bound(node(x, 0, 0));
if(it != s.end() && it -> l == x) return it;
--it;
int l = it -> l, r = it -> r, v = it -> v;
s.erase(it);
s.insert({l, x - 1, v});
return s.insert({x, r, v}).first;
}
void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
for(auto it = itl; it != itr; it++) {
cnt[it -> v][it -> v] -= (it -> r) - (it -> l);
if(it != s.begin()) cnt[prev(it) -> v][it -> v] --;
}
if(itr != s.end()) cnt[prev(itr) -> v][itr -> v]--;
s.erase(itl, itr);
auto p = s.insert({l, r, v}).first;
if(p != s.begin()) cnt[prev(p) -> v][p -> v] ++;
if(p != prev(s.end())) cnt[p -> v][next(p) -> v] ++;
cnt[v][v] += r - l;
}
int main() {
cin.tie(0), cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> m >> k;
for(int i = 1; i <= n; i++) {
char ch; cin >> ch;
a[i] = ch - 'a';
if(i != 1) cnt[a[i - 1]][a[i]]++;
}
for(int i = 1; i <= n; i++) {
int j = i; while(j + 1<= n && a[j + 1] == a[i]) j++;
s.insert({i, j, a[i]});
i = j;
}
while(m--) {
int op; cin >> op;
int l, r;
char v;
if(op == 1) {
cin >> l >> r >> v;
assign(l, r, v -'a');
}
else {
int ans = 0;
for(int i = 1; i <= k; i++) {
cin >> t[i];
for(int j = 1; j <= i; j++) {
ans += cnt[t[i] - 'a'][t[j] - 'a'];
}
}
cout << ans + 1 << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号