传送门: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; }