Mr.BG Hates Palindrome
思路
-
计算总的可能字符串数:对于给定长度 N N N 和字符种类数 M M M,总的可能字符串数为 M N M^N MN。
-
计算回文字符串数:对于长度为 1 1 1 的字符串,只有 M M M 种可能。对于长度大于 1 1 1 的字符串,回文字符串的数量等于字符种类数 M M M 的 ⌈ N / 2 ⌉ \lceil N/2 \rceil ⌈N/2⌉ 次幂。这是因为对于一个回文字符串,它的前半部分决定了后半部分,所以可以利用字符种类数的次方来计算回文字符串数。
-
计算非回文字符串数:非回文字符串数等于总的可能字符串数减去回文字符串数。即非回文字符串数 = M N − M ⌈ N / 2 ⌉ =M^N-M^{\lceil N/2\rceil} =MN−M⌈N/2⌉。
-
边界情况处理:由于 M ⌈ N / 2 ⌉ M^{\lceil N/2 \rceil} M⌈N/2⌉ 可能大于 M N M^N MN,所以在计算非回文字符串数时要确保结果为正数。可以使用模运算来处理这一点。
代码
#include<bits/stdc++.h> // 超级无敌万能头文件
using namespace std;
#define MOD 1000000007
// 幂计算 (a^b) % mod
long long modPow(long long a, long long b, long long mod) {
long long result = 1;
while (b > 0) {
// 如果 b 是奇数,将当前的 a 乘到结果中
if (b % 2 == 1) {
result = (result * a) % mod;
}
// 平方 a
a = (a * a) % mod;
// 将 b 减半
b /= 2;
}
return result;
}
// 计算回文字符串数
long long sb(int N, int M, long long mod) {
if (N == 1) return M; // 长度为1时,总共有M种可能(即每种字符都算一种)
// 回文字符串的数量 = M^(ceil(N / 2))
long long halfLength = (N + 1) / 2; // 计算N的一半,向上取整
return modPow(M, halfLength, mod);
}
int main() {
int T;
cin >> T;
for (int t = 1; t <= T; t++) {
int N, M;
cin >> N >> M;
// 总的可能的字符串数 = M^N
long long totalStrings = modPow(M, N, MOD);
// 回文字符串数
long long palindromicStrings = sb(N, M, MOD);
// 非回文字符串数 = 总字符串数 - 回文字符串数
long long nonPalindromicStrings = (totalStrings - palindromicStrings + MOD) % MOD;
// 输出
cout << "Case " << t << ": " << nonPalindromicStrings << "\n";
}
return 0;
}
解释
幂计算 modPow
:
该函数用于计算 a b m o d MOD a^b \bmod \text{MOD} abmodMOD,通过快速幂法减少时间复杂度。利用了二分法的思想,将幂次减半,并在幂次为奇数时将当前底数乘到结果里。
计算回文字符串数 sb
:
对于长度为 1 1 1 的字符串,只有 M M M 种可能(每种字符各一个)。
对于长度大于 1 1 1 的字符串,回文字符串的数量等于字符种类数 M M M 的 ⌈ N / 2 ⌉ \lceil N/2 \rceil ⌈N/2⌉ 次幂,因为回文字符串的前半部分确定后,后半部分也就确定了。