题目链接:UOJ - 74
题目分析
题目中,将字符串 S 的第一个字符移到末尾,其他字符向前移动一个位置,f(S) 就从 Hi 变成了 Hi+1。
我们分析一下这个过程:假设第一个字符为 c, (Hi - 26^(n-1) * c) * 26 + c = Hi+1
26 * Hi - Hi+1 = (26^n - 1) * c
c = (26 * Hi - Hi+1) * Inv(26^n - 1)
那么每一个 c 都可以直接解出来,由于题目保证有解,所以每一个 c 解出来的一定都是 [0, 25] 的数。
嗯..看起来非常对..提交上去....WA.50
为什么只得了50分呢?因为:“有除法的时候一定要考虑没有逆元的情况!”
当 (26^n - 1) % p = 0 的时候,26^n - 1 是没有逆元的。而这样的点在数据里有 5 个 【QAQ】。
这样在前面的方程 26 * Hi - Hi+1 = (26^n - 1) * c 中含有 c 的项系数为 0,被消去,变成了 Hi * 26 = Hi+1 。
因为方程与 c 无关,所以 c 是任何一个字母这个方程都可以成立。
那么就要根据 f(S) = Hi 来求出这个 S 了,我们知道 f(S) 相当于是把字符串看做一个 26 进制数,然后这里的 Hi * 26 = Hi+1。
所以我们把 H0 看做26进制数求出对应的字符串,之后向左移一位就相当于将 H 乘了 26 ,依然是符合 f(S) = Hi+1 的。
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; const int MaxN = 100000 + 5; typedef long long LL; int n, p, t; int H[MaxN]; LL A0, Temp; LL X[MaxN]; LL Pow(LL a, int b) { LL ret = 1, f = a; while (b) { if (b & 1) { ret *= f; ret %= p; } b >>= 1; f *= f; f %= p; } return ret; } LL NY(LL x) { x = ((x % p) + p) % p; return Pow(x, p - 2); } int main() { scanf("%d%d", &n, &p); for (int i = 0; i < n; ++i) scanf("%d", &H[i]); if (Pow(26ll, n) != 1) { Temp = NY(Pow(26ll, n) - 1); for (int i = 0; i < n; ++i) { if (i == n - 1) t = 0; else t = i + 1; X[i] = ((LL)H[i] * 26 - (LL)H[t]) % p * Temp; X[i] = ((X[i] % p) + p) % p; } } else { int Pos = n; while (H[0]) { X[--Pos] = H[0] % 26; H[0] /= 26; } } for (int i = 0; i < n; ++i) printf("%c", 'a' + X[i]); printf(" "); return 0; }