bzoj2160: 拉拉队排练(manacher)
题目描述:艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。
输入格式:输入为标准输入。第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。
输出格式:输出为标准输出。输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。
样例输出:
5 3
ababa
样例输出:
45
【样例说明】
和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为。
解析:一道比较简单的题。
看到回文串和n的范围,就可以想到用manacher。
注意这题只需要求出长度为奇数的回文串即可,所以在进行manacher时不用多添加字符。
由于k的范围较大,所以肯定不能单纯拿出k个乘起来。
考虑到每个数都会对后面的数产生贡献,所以可以维护一个类似前缀和的东西,再用快速幂优化。
代码如下:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int MOD = 19930726;
const int maxn = 1e6 + 5;
int n, mr, mid, hw[maxn];
ll bt[maxn], k;
char s[maxn];
void manacher(void) {
for (int i = 1; i <= n; ++ i) {
if (i < mr) hw[i] = min(hw[mid * 2 - i], mr - i);
else hw[i] = 1;
while (i - hw[i] >= 0 && i + hw[i] <= n && s[i - hw[i]] == s[i + hw[i]]) hw[i] ++;
if (i + hw[i] - 1 > mr) {
mr = i + hw[i] - 1;
mid = i;
}
}
}
int ksm(int x, ll y) {
int res = 1, base = x;
while (y > 0) {
if (y & 1) res = 1ll * res * base % MOD;
base = 1ll * base * base % MOD;
y >>= 1;
}
return res;
}
int main() {
scanf("%d %lld", &n, &k); scanf("%s", s + 1);
manacher();
for (int i = 1; i <= n; ++ i) bt[hw[i] * 2 - 1] ++;
ll ans = 1, sum = 0;
if (!(n & 1)) n --;
for (int i = n; i > 0 ; i -= 2) {
sum += bt[i];
if (k < sum) {
ans *= ksm(i, k);
ans %= MOD;
break;
}
else ans *= ksm(i, sum);
ans %= MOD;
k -= sum;
}
if (sum < k) return puts("-1"), 0;
printf("%lld", ans);
return 0;
}