Codeforces Round 934 2D/1B

Lu_xZ發表於2024-03-17

Link

場上思路出的最快的一題,但沒調出來

反著考慮全為迴文串需滿足哪些情況。

\(k = 1\),沒有限制條件。

\(k = 2\),對於任意三個位置 _ _ _,先填 \(x\) \(x\) _,然後二三也要回文,第三位只能是 \(x\),最終整段區間全部相同。

\(k = 3\),全部相同的情況肯定滿足,考慮出現不同元素:

  • _ _ _ _
  • \(x\) _ \(x\) _
  • \(x\) \(y\) \(x\) _

此時二到四段也要回文,最終 \(x\) \(y\) \(x\) \(y\) 交替出現。

以此類推 \(k > 3\) 的情況,得到結論:

奇數需間隔排列或全相等,偶數只能全相等

此處有一特殊情況,若 \(k = r - l + 1\),那麼只要整段不迴文,就有 \(r - l + 1\) 的貢獻。

如何快速判斷區間全部相等?

只需維護 \(lst[i]\) 表示前一個與 \(s[i]\) 不同的元素的位置,最後判斷 \(lst[r] < l\)
具體實現:

	vector<int> lst(n, -1);
	for(int i = 1; i < n; ++ i) {
		if(s[i] != s[i - 1]) lst[i] = i - 1;
		else lst[i] = lst[i - 1];
	}

如何快速判斷元素交替出現?

維護 \(f[i][pre]\) 表示從 \(i\) 起,前一位的值是 \(pre\) 的交替段向左延伸的最大長度。

這裡再維護一個 \(L[i] = i - f[i][s[i - 1]] + 1\) 得到區間左端點,判斷 \(L[r] <= l\)

	vector<vector<int>> f(n, vector<int>(128, 1));
	vector<int> L(n, 0);
	for(int i = 1; i < n; ++ i) {
		f[i][s[i - 1]] = 1 + f[i - 1][s[i]];
		L[i] = i - f[i][s[i - 1]] + 1;
	}

如何判斷區間是否迴文?

可以 \(manacher\),但我不會。
這裡用線段樹 + 字串雜湊解決,不會的可以先看這題 [ABC331F] Palindrome Query

程式碼:

#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
#define All(X) X.begin(), X.end()
using namespace std;
using ll = long long;
constexpr int N = 2e5 + 5, P = 1e9 + 7, B = 131;

int n, m, pw[N] = {1};
string s;

struct Node {
	int h1, h2, sz;
} t[N * 4];

void pushup(Node &x, Node y, Node z) {
	x.h1 = (1ll * y.h1 * pw[z.sz] % P + z.h1) % P;
	x.h2 = (1ll * z.h2 * pw[y.sz] % P + y.h2) % P;
	x.sz = y.sz + z.sz;
}

#define ls x << 1
#define rs x << 1 | 1

void build(int x, int l, int r) {
	if(l == r) {
		t[x] = {s[l], s[l], 1};
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	pushup(t[x], t[ls], t[rs]);
}

Node Query(int x, int l, int r, int L, int R) {
	if(L <= l && r <= R) return t[x];
	Node ret = {0, 0, 0};
	int mid = l + r >> 1;
	if(mid >= L) pushup(ret, ret, Query(ls, l, mid, L, R));
	if(mid < R) pushup(ret, ret, Query(rs, mid + 1, r, L, R));
	return ret;
}

ll pre[N];

void solve() {
	cin >> n >> m;
	cin >> s;
	build(1, 0, n - 1);
	vector<int> lst(n, -1);
	for(int i = 1; i < n; ++ i) {
		if(s[i] != s[i - 1]) lst[i] = i - 1;
		else lst[i] = lst[i - 1];
	}
	vector<vector<int>> f(n, vector<int>(128, 1));
	
	vector<int> L(n, 0);
	for(int i = 1; i < n; ++ i) {
		f[i][s[i - 1]] = 1 + f[i - 1][s[i]];
		L[i] = i - f[i][s[i - 1]] + 1;
	}
	
	for(int i = 0; i < m; ++ i) {
		int l, r; cin >> l >> r;
		-- l, -- r;
		if(lst[r] < l) cout << 0 << '\n';
		else {
			ll len = r - l + 1;
			ll ans = (len - 1) * len / 2;
			ans --;
			if(L[r] <= l) {
				ans -= pre[len - 1];
			}
			Node tmp = Query(1, 0, n - 1, l, r);
			if(tmp.h1 != tmp.h2) ans += len;
			cout << ans << '\n';
		}
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	rep(i, 3, 2e5) pre[i] = pre[i - 1] + (i & 1) * i;
	rep(i, 1, 2e5) pw[i] = (1ll * pw[i - 1] * B) % P;
	int T = 1;
	cin >> T;
	while(T --) solve();
	return 0;
}

相關文章