题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2119
就是找差分序列上中间差 m 的相等的两段。
考虑枚举这样一段的长度 L 。可以把序列分成 \( \frac{n}{L} \) 段;令 L , 2L , ... 这样的位置为关键点,那么每个关键点 i 求一下 LCP( i , i+L+m ) 和 LCS( i , i+L+m ) ,就能知道过这个关键点的左端点的合法范围。用 lst 记录上一个关键点算出的右端点来去重即可。
这样是 nlogn 的,且不会遗漏合法的解。
如果有两个关键点之间的合法左端点,满足该点左边是不合法的左端点,右边也是不合法的左端点,那么这个解就不会被算上。但不会有这样的情况。因为以这个点为左端点的段的长度是 L ,一定跨越了下一个关键点;从下一个关键点找 LCP 一定会覆盖这个左端点。
预处理 LCP 和 LCS 可以把差分序列正着和反着接在一起,中间填一个没出现的最小字符,即 0 ;但平时求 ht[ ] 的时候没有判断 if(rk[i]==1)continue ; ,因为到时候会有 s[ 0+0 ] != s[ i+0 ] ,但此时会有 s[ 0 ] = 0 , s[ i ] = 0 ( rk[ i ] = 1 ) ,所以会求错。需要注意。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
int Mx(int a,int b){return a>b?a:b;}
const int N=1e5+,K=;
int n,m,s[N],sa[N],rk[N],tp[N],tx[N],ht[N][K],bin[K],lg[N];
ll ans;
void Rsort(int n,int nm)
{
for(int i=;i<=nm;i++)tx[i]=;
for(int i=;i<=n;i++)tx[rk[i]]++;
for(int i=;i<=nm;i++)tx[i]+=tx[i-];
for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i];
}
void get_sa(int n)
{
int nm=n+;
for(int i=;i<=n;i++)tp[i]=i,rk[i]=s[i]+;//+1 for s[i]=0!!!
Rsort(n,nm);
for(int k=;;k<<=)
{
int tot=;
for(int i=n-k+;i<=n;i++)tp[++tot]=i;
for(int i=;i<=n;i++)
if(sa[i]>k)tp[++tot]=sa[i]-k;
Rsort(n,nm);memcpy(tp,rk,sizeof rk);
nm=; rk[sa[]]=;
for(int i=;i<=n;i++)
{
int u=sa[i]+k,v=sa[i-]+k;if(u>n)u=;if(v>n)v=;
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-]]&&tp[u]==tp[v])?nm:++nm;
}
if(nm==n)break;
}
}
void get_ht(int n)
{
for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;i<=lg[n];i++)bin[i]=bin[i-]<<;
s[]=N;///////for rk[i]==1 s[0+0]==s[i+0]
for(int i=,j,k=;i<=n;i++)//k=0!!
{
for((k?k--:),j=sa[rk[i]-];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
ht[rk[i]][]=k;
}
for(int t=;t<=lg[n];t++)
for(int i=;i+bin[t]-<=n;i++)
ht[i][t]=Mn(ht[i][t-],ht[i+bin[t-]][t-]);
}
int qry_ht(int l,int r,bool fx)
{
if(l==r)return fx?sa[l]-n:n-sa[l]+;
if(l>r)swap(l,r); int d=lg[r-l];
return Mn(ht[l+][d],ht[r-bin[d]+][d]);//l+1
}
int main()
{
n=rdn();m=rdn();for(int i=;i<=n;i++)s[i]=rdn();
n--;for(int i=;i<=n;i++)s[i]=tp[i]=s[i+]-s[i];
sort(tp+,tp+n+);int tmp=unique(tp+,tp+n+)-tp-;
for(int i=;i<=n;i++)s[i]=lower_bound(tp+,tp+tmp+,s[i])-tp;
s[n+]=;
for(int i=n+,j=n;j;i++,j--)s[i]=s[j];
int len=n*+;
get_sa(len);get_ht(len); for(int L=,lst=;L<=n;L++,lst=)
//for(int i=L;i+L+m<=n;i+=L)
for(int i=;i+L+m<=n;i+=L)
{
int d=i+L+m;
int l2=qry_ht(rk[i],rk[d],);
int l1=qry_ht(rk[len-i+],rk[len-d+],);
int st=Mx(lst+,i-l1+);
int en=i+l2-L;
if(en<st)continue;
lst=en; ans+=en-st+;
}
printf("%lld\n",ans);
return ;
}