- 题解
- ABC242E (∀x∀)
ABC242E.(∀x∀)
- @ 2026-8-31 16:06:05
我代码由我不由AI!!!
关于AI说我思路是错的但我最终AC这件事。
事实上,我的思路的确不比标程简洁。
解题思路
处理回文,很自然地想到用双指针指一头一尾,然后往中间缩。姑且叫指前面的为 ,后面的为 。
对于 索引来说,只要我当前选的字符的字典序小于 那么对于 到 中间的位置我可以随便选任何字母,字典序一定能比 小。
但是注意:当所选字母的字典序等于 时,接下来的选择就需要受字典序限制了。但是,这也等同于直接把 去掉了,也就说不再需要考虑了。这比较像数位 。
因此,很自然地,贡献分成了两个部分:一个是没有顶到 字典序的;反之则是顶到 字典序的。
对于第一种贡献:这十分好做。 中间的 个英文字母随便选即可,但是注意回文,所以说只有一半的位置随便选,还需注意奇偶,但是不麻烦。
其中,最难的是中界处,即第二种贡献。第二种贡献需要在中界处计算,之所以叫中界处而不是中界点,就是因为中界处包含两种情况:奇、偶。当长度为奇数时,中间会剩下一个字符, 都会指在这里,偶数时中间会剩下两个字符, 分别指左、右。当然,你肯定想到了,贡献不就是 S[i] - 'A' + 1 吗?没错,但是需要做修正。
因为,顶着 的字典序时, 可能 这就意味着,最终不会满足字典序小。例如:AZAA,你会发现字母Z不能选,因为不可能是AZZA。所以,你的选择只会在 A ~ Y 间,你的答案需要--。但是不对,例如:BABA,按理来说,最左边的B大于了最右边的A,最终答案应该--,但是有这种情况:BAAB。这是因为中间的AB把字典序掰回来了,毕竟前面的字典序最先考虑,而最前面的B反而在回文的效果下更靠后考虑了。总的来说就是,字符越前,回文越后,字符靠后,回文靠前,最终字典序是看回文靠前的。注意,如果字典序相等,那么其实是不变的。当然,如果字典序又被掰回去的话,你的答案还是需要--的。
(这道题我搞了一个上午+一个下午,头都痛了!讲也很抽象,还是看代码吧......)
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T = 1;
const int N = 2e5 + 10;
const int MOD = 998244353;
int n;
string s;
//快速幂,帮助计算无限制时的中间26的某次方,一般来讲我是不喜欢用pow的
int FastExp(int a, int b) {
if (b == 0) return 1;
int res = 1;
while (b) {
if (b & 1) {
res = (res * a) % MOD;
}
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
void Solve() {
cin >> n >> s;
s = " " + s;
int ans = 0, sp = n % 2;//sp处理奇偶
bool flag = false;
for (int i = 1, j = n; i <= j; i++, j--) {
//下面这个if判断是最难懂也是最重要的
if (s[i] > s[j]) flag = true;
else if (s[i] < s[j] && i != j) flag = false;
if (i == j || i == j - 1) {
if (s[i] >= s[j] && flag) ans--;
ans = (ans + s[i] - 'A' + 1) % MOD;
} else {
ans = (ans + (FastExp(26, (j - i - 1) / 2 + sp) * (s[i] - 'A')) % MOD) % MOD;
}
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--) {
Solve();
}
return 0;
}