没上OEIS真的是亏了。
题目大意:
问有多少个字符集大小为|S|的长度为n的字符串,不存在一个长度大于1的回文后缀。
1<=n<=1000
1<=|S|<mo=1e9+7
题解:
这种题一看就要容斥原理
首先总方案数是S^n
减去后缀是回文串的,但是我们发现直接减肯定减重。
不妨设 f [ i ] f[i] f[i]表示长度为i的回文串,且它也不存在长度大于1的回文后缀的方案数。
A n s = S n − ∑ i = 2 n f [ i ] ∗ S n − i Ans=S^n-\sum_{i=2}^nf[i]*S^{n-i} Ans=Sn−∑i=2nf[i]∗Sn−i
那么 f [ i ] f[i] f[i]如何计算呢?
显然可以继续容斥,即总数减去有回文后缀,且这个回文后缀没有大于1的回文后缀。
设
m
=
(
n
+
1
)
/
2
m=(n+1)/2
m=(n+1)/2
f
[
n
]
=
S
m
−
∑
i
=
1
m
f
[
i
]
∗
S
m
−
i
f[n]=S^m-\sum_{i=1}^mf[i]*S^{m-i}
f[n]=Sm−∑i=1mf[i]∗Sm−i
为什么不用考虑i>m的情况呢,
因为如果后缀i>m是回文的,这个后缀一定有一个大于1的回文后缀。
证明:
因为原串是回文的:
所以s[i…2m-i]是回文的,又因为后缀i是回文的,所以长度是i-(2m-i)+1的后缀也是回文的。
i-(2m-i)+1=2(i-m)+1
因为i>m所以2(i-m)+1>=3
因此后缀i一定还存在长度大于1的回文后缀。
感觉最后的这个点挺难想的,卡了一会儿,毕竟这题Samjia做了1h也没有做出来(小骄傲~)
Code:
#include<cstdio>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e3 + 5;
int n, s, mo;
ll f[N], a[N], ans;
int main() {
scanf("%d %d %d", &n, &s, &mo);
a[0] = 1; fo(i, 1, n) a[i] = a[i - 1] * s % mo;
fo(i, 2, n) {
int m = i + 1 >> 1;
f[i] = a[m];
fo(j, 2, m) f[i] = (f[i] - f[j] * a[m - j] % mo + mo) % mo;
}
ans = a[n];
fo(i, 2, n) ans = (ans - a[n - i] * f[i] % mo + mo) % mo;
printf("%lld", ans);
}