题目大意:
就是现在给出一个长度为 n 的字符串(1 <= n <= 10^6)和一个正整数K(1 <= K <= 10^12)
对于给出的长度为n的字符串如果其回文串的数量比K少则输出-1, 否则输出所有回文串中长度为奇数的最长的前K个回文串的长度的乘积, 结果对于19930726取模输出
大致思路:
首先可以用manacher算法确定每个位置的回文半径, 由于这里只需要长度是奇数, 所以不需要再原来的字符串的相邻两个字符之间插入未出现的字符, 直接在首尾添加好不同字符之后盘一遍manacher算法, 对于位置i为中心的回文半径R[i], 用dp[i]来表示相邻长度的回文串的数量差分(其实就是一个常用的前缀和技巧, 因为这里每次更新[1, R[i]]这个区间 + 1, 而只在所有更新完毕之后才查询所以没有必要使用树状数组, 直接根据每次更新的时候dp[1]++, dp[R[i] + 1]--, 最后后dp[1~i]的和就是最终ans[i]的值, 即长度为2*i - 1的回文串的数量
然后用快速幂就可以了, 没有什么难度
细节就看代码吧
(吐槽一下第一次在BZOJ上交题没在F.A.Q里看到用%I64d还是%lld, 然后我PE了看了好久不知道为什么....最后还是用%lld过了 = =)
话说用%I64d就算是Wrong Answer也判了Presentation Error..
代码如下:
Result : Accepted Memory : 21780 KB Time : 520 ms
/**************************************************************
Problem: 2160
User: Gatevin
Language: C++
Result: Accepted
Time:520 ms
Memory:21780 kb
****************************************************************/
/*
* Author: Gatevin
* Created Time: 2015/3/20 11:20:33
* File Name: Chitoge_Kirisaki.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 1000100
const lint mod = 19930726;
int n;
lint K;
char in[maxn];
int R[maxn];
lint dp[maxn];
lint ans[maxn];
/*
* 这里没有在输入的字符串中间插入特殊字符
* 得到的半径R都是奇数长度的回文串的半径
*/
void Manacher(char *s, int *R, int n)
{
int p = 0, mx = 0;
R[0] = 1;
for(int i = 1; i <= n; i++)
{
if(mx > i)
R[i] = min(R[2*p - i], mx - i);
else R[i] = 1;
while(s[i - R[i]] == s[i + R[i]])
R[i]++;
if(i + R[i] > mx)
p = i, mx = i + R[i];
}
return;
}
lint quick_pow(lint base, lint pow)
{
lint ret = 1;
while(pow)
{
if(pow & 1) ret = (ret*base) % mod;
pow >>= 1;
base = base*base % mod;
}
return ret;
}
int main()
{
scanf("%d %lld", &n, &K);
{
scanf("%s", in + 1);
in[0] = '@'; in[n + 1] = '$'; in[n + 2] = '\0';
Manacher(in, R, n);
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)//所有以i为中心的奇数长度的回文串
{
dp[1]++;
dp[R[i] + 1]--;
}
memset(ans, 0, sizeof(ans));
int maxlen = 0;
for(int i = 1; i <= (n + 1) >> 1; i++)//ans[i]表示长度为2*i - 1的串的个数
{
ans[i] = ans[i - 1] + dp[i];
if(ans[i] > 0) maxlen = i;
}
lint result = 1;
int r = maxlen;
while(r && K > 0)
result = (result*quick_pow((lint)(2*r - 1), min(ans[r], K))) % mod, K -= ans[r], r--;
if(K > 0) printf("-1\n");
else printf("%lld\n", result);
}
return 0;
}