题解:CF610E Alphabet Permutations

模拟赛妙妙题!

题意

最开始有一个原始字符串 \(S\),长为 \(n\),由前 \(k\) 个小写字母组成。

\(m\) 次操作。

  1. 将原序列 \([l, r]\) 赋值 \(c\)

  2. 给定 \(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;
}
posted @ 2025-07-25 22:24  merlinkkk  阅读(8)  评论(0)    收藏  举报