洛谷 P4548 [CTSC2006] 歌唱王国
结论:答案为 \(\sum\limits_{s_{1 \sim k} = s_{m - k + 1 \sim m}} n^k\)。
记一下两种理解方法。
假设有人开了一个赌场,每一秒钟有一位赌徒带着 \(1\) 元进入。
赌场的规则是,每一秒会出现一个数字,每一位赌徒可以用 \(x\) 元钱赌下一秒出现 \(y\),赌对了返还 \(nx\) 元,否则不返还。容易看出这个赌博是公平的。
每位赌徒会用 \(n^{i - 1}\) 元赌下一秒出现 \(s_i\),只要有一次失败赌徒就空手而归。
当 \(s\) 串完整的出现过一次后,赌场就会关门。
由于这个赌博是公平的,所以赌场期望不赚不赔。在关门前赌场支付了 \(\sum\limits_{s_[1, k] = s_[m - k + 1, m]} n^k\) 元,所以期望有这么多个赌徒来过赌场。
考虑对 \(s\) 建 KMP 自动机,设 \(t_{i, c}\) 为 \(s_{1 \sim i}\) 加上一个字符 \(c\) 后转移到的位置,\(f_i\) 为当前唱出了 \(s_{1 \sim i}\),还需要的步数。明确一下我们求的是 \(f_0\)。
有转移:
考虑 \(t_{i, c}\),若 \(c = s_{i + 1}\),那么 \(t_{i, c} = i + 1\),否则 \(t_{i, c} = t_{fail_i, c}\)。可以发现 \(t_i\) 和 \(t_{fail_i}\) 只有 \(s_{i + 1}\) 一位不同。
可得:
设 \(g_i = f_{fail_i} - f_i\),那么有 \(g_{i + 1} = n g_i\)。边界为 \(g_1 = f_0 - f_1 = n\)。因此 \(g_i = n^i\)。
因为 \(f_m = 0\),所以 \(g_m = f_{fail_m} - f_m = n^m\),因此 \(f_{fail_m} = n^m\),\(f_{fail_{fail_m}} = n^m + n^{fail_m}\),依此类推,我们就能得到答案为 \(\sum\limits_{s_{1 \sim k} = s_{m - k + 1 \sim m}} n^k\)。
code
// Problem: P4548 [CTSC2006] 歌唱王国
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4548
// Memory Limit: 250 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 100100;
const int mod = 10000;
inline int qpow(int b, int p) {
int res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
int n, m, a[maxn], fail[maxn];
void solve() {
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
scanf("%d", &a[i]);
fail[i] = 0;
}
for (int i = 2, j = 0; i <= m; ++i) {
while (j && a[j + 1] != a[i]) {
j = fail[j];
}
if (a[j + 1] == a[i]) {
++j;
}
fail[i] = j;
}
int ans = 0;
for (int i = m; i; i = fail[i]) {
ans = (ans + qpow(n % mod, i)) % mod;
}
printf("%04d\n", ans);
}
int main() {
int T = 1;
scanf("%d%d", &n, &T);
while (T--) {
solve();
}
return 0;
}