CF159D
给定一个长度为 \(n\) 的字符串 \(S\),求有多少四元组 \(l_1,r_1,l_2,r_2\) 满足 \(1 \le l_1 \le r_1 < l_2 \le r_2 \le n\) 且 \(S \left[ l_1 \dots r_1 \right],S \left[ l_2 \dots r_2 \right]\) 都是回文字符串。
\(n \le 2000\)
因为 \(n \le 2000\),所以时间复杂度为 \(O(n^2)\) 的 DP 做法可以通过(CF 上的评分是 1500)。
蒟蒻用的是 manacher 和差分。
题目要求的是不相交的回文串的对数。
跑一遍 manacher,求出以字符串中每个字符为中心的最长回文串的半径。
可以发现,对于一个范围是 \(\left[ l,r \right]\) 的回文串,所有范围在 \(\left[ 1,l-1 \right]\) 的回文串都能够与之形成合法的四元组。
假设 \(f_i\) 表示以 \(i\) 开头的回文串个数,\(g_i\) 表示以 \(i\) 结尾的回文串个数,易得结果为 $$\sum_{i=2}^n \sum_{j=1}^{i-1} f_i \times g_j$$
因为在初始化 \(f,g\) 时用到的是区间修改。
所以利用差分解决。
对于一个以 \(i\) 为中心,最长回文半径为 \(l_i\) 的区间修改:
- 数组 \(f\) 的修改区间是 \(\left[ i-l_i+1,i \right]\),所以分别对 \(f_{i-l_i+1},f_{i+1}\) 的差分数组进行修改。
- 数组 \(g\) 的修改区间是 \(\left[ i,i+l_i-1 \right]\),所以分别对 \(g_{i},g_{i+l_i}\) 的差分数组进行修改。
在统计答案时,用一个辅助变量存 \(\sum_{j=1}^{i-1} g_j\) 的值,这样就能省去枚举数组求值的过程。
#include <bits/stdc++.h>
using namespace std;const int N=3e4;char a[N],b[N<<1];int l[N],n=1,m,p,q;long long f[N],g[N],s,ans;
int main(){
scanf("%s",a+1);m=strlen(a+1);for(int i=1;i<=m;i++,n++)b[++n]=a[i];b[0]='~',b[n+1]='#';
for(int i=1;i<=n;i++){l[i]=(q>i)?min(l[(p<<1)-i],q-i):1;while(b[i-l[i]]==b[i+l[i]])++l[i];if(i+l[i]>q)q=(p=i)+l[i];}
for(int i=1;i<=n;i++)f[i-l[i]+1]++,f[i+1]--,g[i]++,g[i+l[i]]--;
for(int i=1;i<=n;i++){f[i]+=f[i-1],g[i]+=g[i-1];if(!(i&1))ans+=s*f[i],s+=g[i];}cout<<ans<<endl;return 0;
}