[ BZOJ 2160 ] 拉拉队排练

\(\\\)

\(Description\)


一个由小写字母构成的长为\(N\)的字符串,求前\(K\)长的奇数长度回文子串长度之积,对\(19930726\)取模后的答案。

  • \(N\in [1,10^6]\)\(K\in [1,10^{12}]\)

\(\\\)

\(Solution\)


  • \(Manacher\)处理出所有位置的回文半径,因为答案要求奇数长度,所以不用插入特殊字符,在原串上直接跑\(Manacher\)就好,回文半径内的以当前点为回文中心的每一个子串都是回文串。
  • 所以开一个长度的桶,只给回文半径对应的最长子串长度打标记,显然一个标记代表着后面所有位置都\(+1\),所以计算的时候,一个长度的回文子串个数是长度标记的前缀和。
  • 要求的\(K\)非常大,快速幂处理。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1000010
#define R register
#define gc getchar
#define mod 19930726
using namespace std;
typedef long long ll;
 
char c,s[N];
 
ll m,res=1,sum;
 
int n,len[N],cnt[N];
 
inline void init(){
  scanf("%d%lld",&n,&m);
  while(!isalpha(c=gc()));
  s[1]=c; s[0]='['; s[n+1]=']';
  for(R int i=2;i<=n;++i) s[i]=gc();
}
 
inline void manacher(){
  for(R int i=1,p=0,mr=0;i<=n;++i){
    len[i]=i>mr?1:min(mr-i+1,len[(p<<1)-i]);
    while(s[i-len[i]]==s[i+len[i]]) ++len[i];
    if(i+len[i]-1>mr){p=i;mr=i+len[i]-1;}
    ++cnt[(len[i]<<1)-1];
  }
}
 
inline ll qpow(ll x,ll t){
  ll res=1;
  while(t){
    if(t&1) (res*=x)%=mod;
    (x*=x)%=mod; t>>=1;
  }
  return res;
}
 
int main(){
  init();
  manacher();
  for(R int i=((n/2)<<1)+1;i>=1;i-=2){
    sum+=(ll)cnt[i];
    if(sum>=m){printf("%lld",(res*qpow(i,m))%mod);return 0;}
    (res*=qpow(i,sum))%=mod; m-=sum;
  }
  puts("-1");
  return 0;
}
posted @ 2018-09-22 14:33  SGCollin  阅读(113)  评论(0编辑  收藏  举报