codeforces 232D. Fence

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


你可能感兴趣的:(后缀数组)