CF17E Palisection(字符串)

给定一个长度为 \(n\) \((n\le 2\times 10^6)\) 的小写字母串。问你有多少对相交的回文子串(包含也算相交)。

相交的回文子串,统计起来是麻烦的,因为相交有 \(8\) 个条件。所以要转化为总的减去不相交的。

看到“不相交”,想到这类问题有一个通常的方法:对于每个 \(i\),求出以 \(i\) 结尾的(回文)子串数 \(l_i\) 和以 \(i\) 开头的(回文)子串数 \(r_i\)\(O(n)\) 跑一遍,统计出“前面的乘后面的个数”,就是不相交的。(感性理解一下。)

形式化的,答案可以记作 \(\sum_{i=1}^n r_i \times \sum_{j=i}^{i-1} l_j\)

\(l_i,\,r_i\) 和总子串对数用到 manacher 算法所求的回文半径,实现上运用差分实现 \(O(1)\) 区间加。

下面是 AC 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
const int P=51123987;
char a[N],s[N<<1];
ll n,m=1,mx,mid,ans,sum,p[N<<1],l[N<<1],r[N<<1];
int main(){
	scanf("%I64d%s",&n,a),s[0]='$';
	for(int i=0;i<n;++i,++m) s[++m]=a[i];
	for(int i=1;i<=m;++i){
		p[i]=mx>i?min(p[mid*2-i],mx-i):1;
		while(i+p[i]<=m&&i-p[i]>0&&s[i+p[i]]==s[i-p[i]]) ++p[i];
		if(i+p[i]>mx) mx=i+p[i],mid=i;
		(ans+=p[i]/2)%=P;
	}
	ans=(ans-1)*ans/2%P;
	for(int i=1;i<=m;++i){
		++l[i-p[i]+1],--l[i+1];
		++r[i],--r[i+p[i]];
	}
	for(int i=1;i<=m;++i){
		l[i]+=l[i-1],r[i]+=r[i-1];
		if(i%2==0) (ans-=1ll*sum*l[i]%P)%=P,(sum+=r[i])%=P;
	}
	printf("%I64d\n",(ans+P)%P);
	return 0;
}

THE END

posted @ 2021-08-25 22:14  q0000000  阅读(32)  评论(0编辑  收藏  举报