P1659 [国家集训队] 拉拉队排练 踢姐
简要题意
给定一个长度为 \(n\) 的字符串 \(S\) ,求将 \(S\) 的所有长度为奇数的回文子串按照长度从大到小排序后,取出前 \(k\) 个回文子串,并输出这 \(k\) 个回文子串长度的乘积对 \(19930726\) 取模的结果,若不足 \(k\) 个符合要求的回文子串则输出 -1。
数据范围:
\(1\le n \le 10^6,1\le K \le 10^12\)
思路分析
我们首先想到可以使用Manacher来实现 \(\text O (n)\) 求出所有以 \(i\) 为回文中心时可以构成最长的回文串长度 \(p_i\),但如何实现答案统计呢?显然的,我们直接遍历 \(p_i\) 是错误的,因为我们最多统计到 \(n\times 2\) 个回文串,不难发现会存在回文串中包含回文串的情况(真的不难发现么?真的不难),可以知道若存在一个回文字符串 \(S[x:y]\) 且其长度为 \(p_i\ge 1\) ,则对于任意整数 \(c \in [0,\lfloor \frac{p_i}{2} \rfloor]\) ,该字符串长度为 \(p_i-c\times 2\)的子串 \(S[x+c:y-c]\) 一定为回文串,简证:
由于字符串 \(S[x:y]\) 是回文串,根据定义有:
考虑其子区间 \(l_c = [x+c,y-c]\) ,其长度为 \(L=p_i-2c\) ,由于 \(c\le \lfloor \frac{p_i}{2} \rfloor\) ,有 \(L \ge 0\) 。
对于其任意位置 \(j \in [0,L-1]\) ,考虑其在子串中对称位置:
由于 \(c+j \in [c,c+L-1]=[c,p_i-c-1]\subseteq [0,p_i-1]\) ,则根据原字符串的回文性质可得:
故该字符串的所有非空子串都是回文串。
有了这个结论,现在就开朗多了,那么我们还有没有没有统计到的回文串呢,我们大可以直接假设回文串的个数为 \(\sum\limits^{2n}_{i=0}{\lceil \frac{p_i}{2} \rceil}\) ,使用反证法证明其是对的:
情况1:假设回文串个数大于 \(\sum\limits^{2n}_{i=0}{\lceil \frac{p_i}{2} \rceil}\)
根据鸽巢原理,可知必然存在一个回文子串 \(P\) ,无法被Manacher中心位置 \(i\) 的 \(\lceil \frac{p_i}{2} \rceil\) 个回文子串覆盖,设回文子串为 \(P[l:r]\) ,其回文中心为 \(mid=\frac{l+r}{2}\) 。
由于 \(P\) 是回文串,在manacher中,位置 \(1\le mid \le 2n\) 的 \(p_{mid}\) 至少为 \(r-l+1\) ,故 \(P\) 一定包含在回文中心为 \(mid\) 且长度为 \(p_c\) 的回文串中,根据上文我们证明的结论,以 \(mid\) 为中心,半径在 \([1,p_c]\) 的回文串一定都包含在 \(\lceil \frac{p_v}{2} \rceil\) 计数中,因为算法中我们一定完全遍历过 \(1\sim 2n\) ,且 \(mid \in [1,2n]\),故对于所有可能的回文中心,要么是不合法的越界点,要么是已经被遍历过的点,故不存在该情况。
情况2:假设回文串个数小于 \(\sum\limits^{2n}_{i=0}{\lceil \frac{p_i}{2} \rceil}\)
根据抽屉原理,必然存在至少一个位置 \(i\) ,其 \(\lceil \frac{p_i}{2} \rceil\) 个回文子串中有至少一个不是原字符串回文子串。
但我们知道,对于Manacher算法,必然会更新出每一位可以构成的最长回文串长度,根据上文得到的结论,每一个回文串都一定包含 \(\lceil \frac{p_i}{2} \rceil\) 个回文子串,故不存在某一个位置上存在一个虚假的回文子串。
那么这一题就结束了,我们已知整个字符串存在一共 \(\sum\limits^{2n}_{i=0}{\lceil \frac{p_i}{2} \rceil}\) 个回文子串,我们只需要统计长度不同的回文子串分别有多少个即可,根据上文的结论,我们得出一个回文串 \(S[x:y]\) ,其长度为原来减去 \(2i\) 的子串 \(S[x+i:y-i]\) 为回文串,那么一个长度为 \(p_i\) 的回文串会产生 \(\lceil \frac{p_i}{2} \rceil\) 个长度为原来减去 \(2c\) 的回文子串 \(S[x+c:y-c]\) ,那么我们只需要开一个差分数组完成离线区间操作再前缀和统计答案即可。
代码实现
我们只在意长度为奇数的回文串,由于回文串产出的子回文串长度与原回文串长度奇偶性相同,故直接统计即可,对于前缀和只需要跳过偶数位统计,或跳过偶数位进行前缀和都可以,介于这题数据范围比较大,故使用一下快速幂进行优化,还是怕黑成碳。
🐎:
signed main(){#ifdef Zyhxfreopen("hack.in","r",stdin);#endifios::sync_with_stdio(0),cin.tie(0);int i,j,k,l,r,x,y,z;cin>>m>>k>>g;s[0]='#',s[n=1]='|';for(i=0;i<m;++i) s[++n]=g[i],s[++n]='|';s[++n]='$'; mx=-1;// 预处理扩展串for(i=2;i<n;++i){if(i<=rt) p[i]=min(p[(md<<1)-i],rt-i+1);else p[i]=1;for(;i+p[i]<=n&&i-p[i]>=0&&s[i+p[i]]==s[i-p[i]];++p[i]);if(i+p[i]>rt) rt=i+p[i]-1,md=i;//传统Manacher流程if((p[i]-1)&1)++b[p[i]-1],mx=max(mx,p[i]-1),tot+=p[i]/2+1;//进行差分(思想)}if(tot<k) cout<<-1<<endl,exit(0);// 判断不够的情况for(i=mx;i>=1;i-=2)b[i]+=b[i+2];// 前缀和for(i=mx;i>=1;i-=2){if(k-b[i]<0){ans=(ans*(1ll*qp(i,k)%M3)%M3)%M3,ans%=M3; break;}else{k-=b[i];ans=(ans*(1ll*qp(i,b[i])%M3)%M3)%M3,ans%=M3;}}cout<<ans<<endl;return 0;
}