2119: 股市的预测
题意:
问差分后相隔为b的相同子串对数
题解:
好题啊
首先枚举子串的长度L
有一个不错的思路是每隔L放一个障碍点,对于每个点统计穿过这个点的答案个数,能做到不重不漏
对于关键点i,设
求出的lcs和的lcp即可得出答案
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
#define LLu unsigned long long
using namespace std;
const LLu base=2333;
int n,B,A[50010],a[50010];
LL ans=0;
LLu pre[50010],c[50010];
LLu Hash(int l,int r) {return c[r]-c[l-1]*pre[r-l+1];}
bool check(int l1,int r1,int l2,int r2)
{
if(l1<1||l2<1||r1>n||r2>n) return false;
return Hash(l1,r1)==Hash(l2,r2);
}
int lcs(int l,int r)
{
int ans,L=1,R=n;
while(L<=R)
{
int mid=(L+R)/2;
if(check(l,l+mid-1,r,r+mid-1)) ans=mid,L=mid+1;
else R=mid-1;
}
return ans;
}
int lcp(int l,int r)
{
int ans,L=1,R=n;
while(L<=R)
{
int mid=(L+R)/2;
if(check(l-mid+1,l,r-mid+1,r)) ans=mid,L=mid+1;
else R=mid-1;
}
return ans;
}
int main()
{
scanf("%d %d",&n,&B);
pre[0]=1;for(int i=1;i<=50000;i++) pre[i]=pre[i-1]*base;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i-1]=a[i]-a[i-1];n--;
for(int i=1;i<=n;i++) c[i]=c[i-1]*base+a[i];
//for(int i=1;i<=n;i++) printf("%d ",a[i]);printf("\n");
for(int l=1;l*2+B<=n;l++)
for(int j=1;j+l+B<=n;j+=l)
{
if(a[j]==a[j+B+l])
{
int L=lcs(j,j+B+l),R=lcp(j,j+B+l);
L=min(L,l);R=min(R,l);
ans+=max(L+R-l,0);
//printf("%d %d %d %d\n",j,j+B+l,L,R);
}
}
printf("%lld",ans);
}