11.22 NOIP 模拟赛 T1. 破乱的诗歌
思路
场上考虑的其实很有道理来着
考虑左右字符集已经相同的情况是简单的
否则一定要把左右字符集调整到相同
那我们首先不难发现一个至少要使用的区间, 计算方法比较复杂, 但是宗旨是把左右字符集调整到相同且可匹配的必要花费
然后我们应该拓展长的那一端, 直到字符集相同之后可以考虑断开, 以此来获得最优解
#include <bits/stdc++.h>
#define int long long
#define rs rrt, mid + 1, r
#define mid ((l + r) >> 1)
#define ls lrt, l, mid
#define lrt rt << 1
#define rrt lrt | 1
char s[1000005];
std::vector <int> pos[26];
int n, cnt[26], num[26], id[26];
int ans = 0, m;
signed main () {
scanf("%lld%s", &n, s + 1); m = (n + 1) / 2;
for (int i = 1; i <= n; i++) cnt[s[i] - 'a']++;
for (int i = 1; i <= n; i++) pos[s[i] - 'a'].push_back (i);
int L = n / 2 + 1, R = m;
memset (num, 0, sizeof num);
for (int i = 1; i <= n / 2; i++) num[s[i] - 'a']++;
for (int i = 0; i < 26; i++) if (num[i] > cnt[i] / 2)
L = std::min (L, pos[i][cnt[i] / 2]);
memset (num, 0, sizeof num);
for (int i = n; i > m; i--) num[s[i] - 'a']++;
for (int i = 0; i < 26; i++) if (num[i] > cnt[i] / 2)
R = std::max (R, pos[i][cnt[i] / 2 - 1]);
ans = R - L + 1;
/*先换过来*/
std::vector<int> chg1, chg2; chg1.clear(), chg2.clear();
memset (num, 0, sizeof num);
for (int i = 1; i <= n / 2; i++) num[s[i] - 'a']++;
for (int i = 1; i <= n / 2; i++) if (num[s[i] - 'a'] > cnt[s[i] - 'a'] / 2) chg1.push_back(i), num[s[i] - 'a']--;
memset (num, 0, sizeof num);
for (int i = n; i > m; i--) num[s[i] - 'a']++;
for (int i = n; i > m; i--) if (num[s[i] - 'a'] > cnt[s[i] - 'a'] / 2) chg2.push_back(i), num[s[i] - 'a']--;
for (int i = 0; i < chg1.size(); i++) { int p1 = chg1[i], p2 = chg2[i]; std::swap(s[p1], s[p2]); }
memset (num, 0, sizeof num);
for (int i = 1; i <= n / 2; i++) num[s[i] - 'a']++;
for (int i = n; i > m; i--) num[s[i] - 'a']--;
int ccnt0 = 0;
for (int i = 0; i < 26; i++) ccnt0 += (num[i] == 0);
// std::cout << ccnt0 << ' ';
if (R - m > n / 2 - L + 1) {
// std::cout << "#1: ";
int len = R - m;
memset (num, 0, sizeof num);
int cnt0 = 0;
for (int i = m + 1; i <= R; i++) num[s[i] - 'a']++;
for (int i = n / 2 - len + 1; i <= n / 2; i++) num[s[i] - 'a']--;
for (int i = 0; i < 26; i++) cnt0 += (num[i] == 0);
int l = n / 2 - len + 1, r = R;
while (cnt0 != 26 && l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
if (num[s[l] - 'a'] == 0) cnt0--;
if (num[s[r] - 'a'] == 0) cnt0--;
if (++num[s[l] - 'a'] == 0) cnt0++;
if (--num[s[r] - 'a'] == 0) cnt0++;
}
ans++;
}
while (l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
num[s[l] - 'a']++, num[s[r] - 'a']--; cnt0 = 24;
ans++;
while (cnt0 != 26 && l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
if (num[s[l] - 'a'] == 0) cnt0--;
if (num[s[r] - 'a'] == 0) cnt0--;
if (++num[s[l] - 'a'] == 0) cnt0++;
if (--num[s[r] - 'a'] == 0) cnt0++;
}
ans++;
}
}
}
printf("%lld", ans);
} else {
// std::cout << "#2: ";
int len = n / 2 - L + 1;
memset (num, 0, sizeof num);
int cnt0 = 0;
for (int i = m + 1; i <= m + len; i++) num[s[i] - 'a']--;
for (int i = L; i <= n / 2; i++) num[s[i] - 'a']++;
for (int i = 0; i < 26; i++) cnt0 += (num[i] == 0);
int l = L, r = m + len;
// std::cout << l << ' ' << r << '\n';
while (cnt0 != 26 && l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
if (num[s[l] - 'a'] == 0) cnt0--;
if (num[s[r] - 'a'] == 0) cnt0--;
if (++num[s[l] - 'a'] == 0) cnt0++;
if (--num[s[r] - 'a'] == 0) cnt0++;
}
ans++;
}
while (l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
num[s[l] - 'a']++, num[s[r] - 'a']--; cnt0 = 24;
ans++;
while (cnt0 != 26 && l > 1 && r < n) {
l--, r++;
if (s[l] != s[r]) {
if (num[s[l] - 'a'] == 0) cnt0--;
if (num[s[r] - 'a'] == 0) cnt0--;
if (++num[s[l] - 'a'] == 0) cnt0++;
if (--num[s[r] - 'a'] == 0) cnt0++;
}
ans++;
}
}
}
printf("%lld", ans);
}
return 0;
}
总结
你说的对, 但是码量真大吧
这个有点吃状态的

浙公网安备 33010602011771号