作为礼物的字符串
首先这道题最暴力的想法就是给每个相同的字符在并查集上合并,最后暴力判断,但是这样复杂度是 \(O(n^2)\) 会得到一个 TLE
。
由于并查集具有可以合并的性质,因此可以使用神奇的 倍增 。
对于一个长度为 \(n\) 的回文串 \(s\) 来说,如果我们复制一份原字符串到末尾,那么对于一个回文子串 \(s(l,r)\),那么 \(s(l,r) = s(n + l, n + r)\),那么我们可以用一个并查集 fa[u][k]
表示 以 \(u\) 为起点,长度为 \(2 ^ k\) 的子串的根。
即如果 \(fa[i][k] == fa[j][k]\) 那么 \(s(i, i + 2^k - 1) == s(j, j + 2^k - 1)\)
然后对于最后的 \(fa[u][k] = pos\) 我们可以将长度为 \(2^k\) 的子串拆分成两个长度为 \(2^{k-1}\)的子串,然后重新进行 merge
,如果最后判断长度为 \(2^0\) 的所有字符是否符合回文串的条件即可。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 2E5 + 10;
int fa[N][20];
int find(int x, int k) {
if (fa[x][k] != x) fa[x][k] = find(fa[x][k], k);
return fa[x][k];
}
void merge(int x, int y, int k) {
x = find(x, k), y = find(y, k);
if (x != y) fa[x][k] = y;
}
void solve() {
int n, m; cin >> n >> m;
int bit = __lg(n * 2);
for (int i = 1; i <= n * 2; i ++ ) {
for (int j = 0; j <= bit; j ++ ) {
if (j == 0)
fa[i][j] = i <= n ? i : 2 * n - i + 1;
else
fa[i][j] = i;
}
}
while (m -- ) {
int l1, r1; cin >> l1 >> r1;
int l2 = 2 * n - r1 + 1, r2 = 2 * n - l1 + 1;
for (int k = bit; k >= 0; k -- ) {
if (l1 + (1 << k) - 1 <= r1) {
merge(l1, l2, k);
l1 += 1 << k;
l2 += 1 << k;
}
}
}
for (int k = bit; k >= 1; k -- ) {
for (int i = 1; i + (1 << k) - 1 <= 2 * n; i ++ ) {
int pos = find(i, k);
merge(i, pos, k - 1);
merge(i + (1 << k - 1), pos + (1 << k - 1), k - 1);
}
}
int cnt = 0;
for (int i = 1; i <= n; i ++ ) if (find(i, 0) == find(n - i + 1, 0)) {
++ cnt;
}
if (cnt == n)
cout << "YES\n";
else
cout << "NO\n";
cout << cnt << "\n";
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
solve();
return 0;
}