bzoj4650: [Noi2016]优秀的拆分

Posted f321dd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4650: [Noi2016]优秀的拆分相关的知识,希望对你有一定的参考价值。

考场上没秒的话多拿5分并不划算的样子。

思想其实很简单嘛。

要统计答案,求以每个位置开始和结束的AA串数量就好了。那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点。计算出相邻关键点的最长公共前后缀,把对应的位置区间加一下。

求lcp和lcs可以用后缀数组,也可以用hash。

#include<bits/stdc++.h>
#define N 30005
#define M (l+r+1>>1)
using std::min;
const int m=1000000097;
int i,j,n,t,l,r,p,q;
int f[N],a[N],u[N],v[N];
char s[N];
int hash(int i,int j){
	return(f[j]-1ll
	*f[i]*a[j-i]%m+m)%m;
}
int main(){
	for(a[0]=i=1;i<N;++i)
		a[i]=223ll*a[i-1]%m;
	scanf("%d",&t);
	while(t--){
		scanf("%s",s+1);
		n=strlen(s+1);
		for(i=1;i<=n;++i){
			u[i]=v[i]=0;
			f[i]=(223ll
			*f[i-1]+s[i])%m;
		}
		for(i=1;i*2<=n;++i)
			for(j=1;j+i<=n;j+=i){
				l=0,r=min(i,j);
				while(l^r)
					hash(j+i-M,j
					+i)^hash(j-M,
					j)?r=M-1:l=M;
				p=j-l+1;
				l=0,r=min(i-1,n-j-i);
				while(l^r)
					hash(j+i,j+i
					+M)^hash(j,j
					+M)?r=M-1:l=M;
				q=j+l+1;
				if(p+i<=q){
					++u[p],--v[q+i];
					--u[q-i+1];
					++v[p+i*2-1];
				}
			}
		long long k=0;
		for(i=1;i<=n;++i){
			u[i]+=u[i-1];
			v[i]+=v[i-1];
			k+=u[i]*v[i-1];
		}
		printf("%lld\n",k);
	}
}

  

以上是关于bzoj4650: [Noi2016]优秀的拆分的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 SA ST表

BZOJ 4650 [Noi2016]优秀的拆分:后缀数组

[bzoj4650][Noi2016]优秀的拆分——后缀数组

BZOJ 4650 [Noi2016]优秀的拆分

bzoj4650: [Noi2016]优秀的拆分

BZOJ4650&UOJ219优秀的拆分(二分,hash)