【BZOJ 2160】 拉拉队排练
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=2160
【算法】
先简化题意 : 给定一个字符串,求最长的k个奇回文子串长度的乘积
先运行Manacher算法,对于每个位置i,我们知道以i为中心的回文串的最长半径为pi,那么i这个位置对半径为1-p[i]的回文串的个数都产生了1的”贡献“
因此,我们可以用差分求出任意半径的回文串个数,然后统计答案即可,注意要用快速幂
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 1000010 const int P = 19930726; typedef long long ll; int i,len,n; ll sum,ans,k; int p[MAXN]; ll cnt[MAXN]; char s[MAXN]; inline ll power(ll a,ll n) { ll ans = 1,b = a; while (n) { if (n & 1) ans = 1ll * ans * b % P; n >>= 1; b = 1ll * b * b % P; } return ans; } inline void Manacher() { int i,pos = 0,mx = 0; for (i = 1; i <= len; i++) { if (mx > i) p[i] = min(p[2*pos-i],mx-i); else p[i] = 1; while (i - p[i] >= 1 && i + p[i] <= len && s[i-p[i]] == s[i+p[i]]) p[i]++; if (i + p[i] - 1 > mx) { mx = i + p[i] - 1; pos = i; } } } int main() { scanf("%d%lld%s",&n,&k,s+1); len = strlen(s+1); Manacher(); for (i = 1; i <= len; i++) { cnt[1]++; cnt[p[i]+1]--; } for (i = 1; i <= (len + 1) / 2; i++) cnt[i] += cnt[i-1]; sum = 0; ans = 1; for (i = (len + 1) / 2; i >= 1; i--) { if (cnt[i] <= 0) continue; if (sum + cnt[i] < k) { sum += cnt[i]; ans = 1ll * ans * power(2*i-1,cnt[i]) % P; } else { ans = 1ll * ans * power(2*i-1,k-sum) % P; sum += cnt[i]; break; } } if (sum < k) printf("-1\n"); else printf("%lld\n",ans); return 0; }