BZOJ 2160 拉拉队排练
2160: 拉拉队排练
Description
艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。
Input
输入为标准输入。第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。
Output
输出为标准输出。输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。
Sample Input
ababa
Sample Output
【样例说明】
和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为。
HINT
总共20个测试点,数据范围满足:
这本不是一道难题。但是,让我调了很久。我也很崩溃啊!
考虑到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……
嗯。就是这样。来自黑龙江省哈尔滨市某大附中的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建法和“滚雪球”方法上略有不同外,其余基本相同。但是串长了一倍,时间当然会慢一倍。