艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。
输入为标准输入。第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。
输出为标准输出。输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。
总共20个测试点,数据范围满足: ![技术分享](http://www.lydsy.com/JudgeOnline/images/2160.jpg)
这本不是一道难题。但是,让我调了很久。我也很崩溃啊!
考虑到Manacher是求得以i为回文中心的最长回文子串, 所以假设存在长度为len的回文串, 就必定存在len - 2的回文串, 所以我们用累加的方式记录长度为i的回文串有多少个,然后就取前k个快速幂。
我们可以使用“滚雪球”的方法,一个位置代表的回文串可以长为p,那么它也可以长为p-2。就是这样。LMY当时怒怼YYR,说这就是O(n)的。YYR呵呵一笑,说你讲吧,讲了就知道了。LMY于是讲了下去,忽然发现……
“快速幂是什么鬼!!!(捂嘴)……我们可以预处理出可能用到的幂值。”
额。因为快速幂,这个算法是O(nlog n)的。
关于奇数串。因为题目要求,不用补出‘#‘,即是单倍串。但是,补出‘#‘,变成双倍串也可以。当时WA个不停,最后才发现,manacher里头std::min被我打成了std::max。然后单倍串交上去AC了,但是双倍串卡了特别久。一交上去就RE。
ZJC问:
“你的pal开双倍了吗?”
我回答,开了啊。然后继续查错,最后发现……我的pos没有开双倍。
我A了以后,告诉ZJC。TA呵呵一笑,说LIUWENDING帮他查错,问他开long long没有,他义愤填膺地说print就是%lld,最后发现他开了int……
![技术分享](http://image.mamicode.com/info/201707/20180110235845770911.png)
嗯。就是这样。来自黑龙江省哈尔滨市某大附中的liuwending(至少BZOJ是这么说的)。
最后当然改出来了。
这是单倍串。
1 /**************************************************************
2 Problem: 2160
3 User: Doggu
4 Language: C++
5 Result: Accepted
6 Time:276 ms
7 Memory:9612 kb
8 ****************************************************************/
9
10 #include<cstdio>
11 #include<cstring>
12 #include<algorithm>
13 const int S = 1000005;
14 const long long MOD=19930726;
15 int n, pal[S], pos[S];
16 long long k;
17 char P[S];
18 long long mpow(long long a,long long b) {
19 long long res;
20 for( res = 1; b; b>>=1, a=a*a%MOD ) if(b&1) res=res*a%MOD;
21 return res;
22 }
23 void manacher() {
24 int lenp = strlen(P), mx = 0, id;
25 for( int i = 1; i < lenp; i++ ) {
26 if(i<=mx) pal[i]=std::min(mx-i+1,pal[2*id-i]);
27 else pal[i]=1;
28 while(P[i-pal[i]]==P[i+pal[i]]) pal[i]++;
29 if(i+pal[i]-1>=mx) {
30 mx=i+pal[i]-1, id=i;
31 }
32 pos[pal[i]*2-1]++;
33 }
34 long long sum = 0, tot = 0, ans = 1;
35 for( int i = lenp; i > 0; i-- ) if(i&1) {
36 sum += pos[i];
37 ans=ans*mpow(i,std::min(sum,k-tot))%MOD;
38 tot += sum;
39 if(tot>k) printf("%lld",ans), std::exit(0);
40 }
41 printf("-1\n");
42 }
43 int main() {
44 scanf("%d%lld%s",&n,&k,P+1);
45 P[0]=‘$‘;
46 manacher();
47 return 0;
48 }
49
这是双倍串。
1 /**************************************************************
2 Problem: 2160
3 User: Doggu
4 Language: C++
5 Result: Accepted
6 Time:504 ms
7 Memory:19396 kb
8 ****************************************************************/
9
10 #include <cstdio>
11 #include <cstring>
12 #include <algorithm>
13 const int S = 1001000;
14 const long long MOD = 19930726;
15 int pal[S*2], pos[S*2];
16 long long k;
17 char s[S], P[S*2];
18 long long mpow(long long a,long long b) {
19 long long res;
20 for( res = 1; b; b>>=1, a=a*a%MOD ) if(b&1) res=res*a%MOD;
21 return res;
22 }
23
24 void build(char *ss) {
25 int lens = strlen(ss);
26 for( int i = 0; i < lens; i++ ) P[i*2+1]=‘#‘, P[i*2+2]=ss[i];
27 P[0]=‘+‘;P[lens*2+1]=‘#‘;P[lens*2+2]=‘-‘;P[lens*2+3]=‘\0‘;
28 }
29 void manacher() {
30 int lenp = strlen(P), mx = 0, id;
31 for( int i = 1; i < lenp; i++ ) {
32 if(i<=mx) pal[i]=std::min(mx-i+1,pal[2*id-i]);
33 else pal[i]=1;
34 while(P[i-pal[i]]==P[i+pal[i]]) pal[i]++;
35 if(i+pal[i]-1>mx) {
36 mx=i+pal[i]-1, id=i;
37 }
38 }
39 for( int i = 2; i < lenp; i+=2 ) pos[pal[i]-1]++;
40 long long sum = 0, tot = 0, ans = 1;
41 for( int i = lenp; i > 0; i-=2 ) {
42 sum += pos[i];
43 ans=ans*mpow(i,std::min(sum,k-tot))%MOD;
44 tot += sum;
45 if(tot>k) printf("%lld\n",ans), std::exit(0);
46 }
47 printf("-1\n");
48 }
49 int main() {
50 scanf("%lld%lld%s",&k,&k,s);
51 build(s);
52 manacher();
53 return 0;
54 }
55
可以发现,它们大体相似。除了在建串、pos建法和“滚雪球”方法上略有不同外,其余基本相同。但是串长了一倍,时间当然会慢一倍。