洛谷 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\)

有转移:

\[f_i = \frac{\sum\limits_{c = 1}^n f_{t_{i, c}}}{n} + 1 \]

考虑 \(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}\) 一位不同。

可得:

\[f_{fail_i} = f_i - \frac{f_{t_{i, s_{i + 1}}}}{n} + \frac{f_{t_{fail_i, s_{i + 1}}}}{n} \]

\[f_{fail_i} - f_i = \frac{f_{fail_{i + 1}} - f_{i + 1}}{n} \]

\(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;
}
posted @ 2023-07-13 15:57  zltzlt  阅读(23)  评论(0编辑  收藏  举报