传送门:http://codeforces.com/problemset/problem/232/D
思路:首先就是差分,设d[i]=a[i+1]-a[i];
然后题目的”匹配"就可以转化为差分数组每一位的和为0
也就是这段区间取相反数之后可以与原区间匹配。
这就可以转化为字符串问题。
设当前询问为(x,y)
把整个串取相反数,再复制到后面,用后缀数组向上向下二分出可行区间(lcp(suffix(l),suffix(x))>=y-x)(注意是y-x,因为这是差分数组)
然后要求不可重叠,就是求rank在(l,r)中有多少串的位置在一个区间内。
用可持久化线段树搞一搞就可以了。
(细节巨多,写的真是想死...)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=200010,maxt=maxn*20,base=1001;
using namespace std;
struct data{int val,id;}suf[maxn];
int n,Q,N,a[maxn],b[maxn],d[maxn],sa[maxn],rank[maxn],sum[maxn],t1[maxn],t2[maxn],s[maxn],st[maxn][20],h[maxn];
bool cmp(data a,data b){return a.val<b.val;}
struct Per_tree{
int tot,son[maxt][2],siz[maxt];
void insert(int k,int p,int l,int r,int x){
if (l==r){siz[k]=siz[p]+1;return;}
int mid=(l+r)>>1;
if (x<=mid){
siz[k]=siz[p]+1,son[k][0]=++tot,son[k][1]=son[p][1];
insert(son[k][0],son[p][0],l,mid,x);
}
else{
siz[k]=siz[p]+1,son[k][1]=++tot,son[k][0]=son[p][0];
insert(son[k][1],son[p][1],mid+1,r,x);
}
}
int query(int k,int l,int r,int x,int y){
if (!k) return 0;
if (l==x&&r==y) return siz[k];
int mid=(l+r)>>1;
if (y<=mid) return query(son[k][0],l,mid,x,y);
else if (x>mid) return query(son[k][1],mid+1,r,x,y);
else return query(son[k][0],l,mid,x,mid)+query(son[k][1],mid+1,r,mid+1,y);
}
int query(int l,int r,int x,int y){return query(r,1,N,x,y)-query(l-1,1,N,x,y);}
}T;
void getsa(){
int *x=t1,*y=t2,p=0,m=0;
for (int i=1;i<=N;i++) suf[i]=(data){s[i],i};
sort(suf+1,suf+1+N,cmp);
for (int i=1;i<=N;i++) sa[i]=suf[i].id;
x[sa[1]]=m=1;
for (int i=2;i<=N;i++){if (s[sa[i]]!=s[sa[i-1]]) m++;x[sa[i]]=m;}
for (int j=1;p<N;j<<=1,m=p){
p=0;
for (int i=N-j+1;i<=N;i++) y[++p]=i;
for (int i=1;i<=N;i++) if (sa[i]>j) y[++p]=sa[i]-j;
memset(sum,0,sizeof(sum));
for (int i=1;i<=N;i++) sum[x[y[i]]]++;
for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
for (int i=N;i;i--) sa[sum[x[y[i]]]--]=y[i];
swap(x,y),x[sa[1]]=p=1;
for (int i=2;i<=N;i++){
if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j]) p++;
x[sa[i]]=p;
}
}
memcpy(rank,x,sizeof(rank));
}
void geth(){
for (int i=1,j=0;i<=N;i++){
if (rank[i]==1) continue;
while (s[i+j]==s[sa[rank[i]-1]+j]) j++;
h[rank[i]]=j;
if (j>0) j--;
}
}
void prework(){
T.tot=N;for (int i=1;i<=N;i++) T.insert(i,i-1,1,N,sa[i]);
for (int i=1;i<=N;i++) st[i][0]=h[i];
for (int i=1;i<=18;i++)
for (int j=1;j+(1<<(i-1))-1<=N;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
}
int getmin(int l,int r){
if (l>r) swap(l,r);
int t=0; l++;
if (l==r) return h[r];
for (;l+(1<<t)<r;t++);
if (l+(1<<t)>r) t--;
return min(st[l][t],st[r-(1<<t)+1][t]);
}
int find(int s,int x,int op){
int l,r,mid;
if (op) l=s,r=N;else l=1,r=s;
while (l!=r){
mid=(l+r)>>1;
if (op) mid++;
if (getmin(mid,s)<x){
if (op) r=mid-1;
else l=mid+1;
}
else{
if (op) l=mid;
else r=mid;
}
}
return l;
}
int query(int l,int r){
int x=find(rank[l],r-l,0),y=find(rank[l],r-l,1);
return T.query(x,y,n+1,n+(l-1)-(r-l))+T.query(x,y,n+(r+1),N);
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<n;i++) d[i]=a[i+1]-a[i];
for (int i=1;i<n;i++) s[i]=d[i];s[n]=-(1e9+10);
for (int i=1;i<n;i++) s[i+n]=-d[i];
N=(n<<1)-1;
/*for (int i=1;i<n;i++) d[i]=a[i+1]-a[i];
for (int i=1;i<n;i++) s[i]=d[i]+base;
s[n]=1;
for (int i=1;i<n;i++) s[n+i]=-d[i]+base;
N=(n<<1)-1;*/
getsa(),geth(),prework(),scanf("%d",&Q);
//for (int i=1;i<=N;i++) printf("hhh%d\n",h[i]);
//for (int i=1;i<=N;i++) printf("rank%d\n",rank[i]);
for (int l,r;Q;Q--){
scanf("%d%d",&l,&r);
if (l==r) printf("%d\n",n-1);
else printf("%d\n",query(l,r));
}
return 0;
}