BZOJ4527: K-D-Sequence【线段树+单调栈】

4527: K-D-Sequence

Description

我们称一个数列为一个好的k-d数列,当且仅当我们在其中加上最多k个数之后,数列排序后为一个公差为d的等差数列。
你手上有一个由n个整数组成的数列a。你的任务是找到它的最长连续子串,使得满足子串为好的k-d数列。

Input

第一行包含三个用空格隔开的整数n,k,d(1n2105;0k2105;0d109)n,k,d(1 \le n\le 2*10^5;0\le k\le 2*10^5;0 \le d \le 10^9)

第二行包含n个空格隔开的整数:a1,a2,...,an(109ai109)a1,a2,...,an(-10^9 \le ai \le 10^9)表示数列a。

Output

输出两个用空格隔开的整数L,r(1Lrn)L,r(1 \le L\le r \le n),表示数列aL,aL+1,...,aRa_L,a_{L+1},...,a_R是好k-d数列的子串中最长的。
如果有多个最优答案,输出那个L值最小的。

Sample Input

6 1 2
4 3 2 8 6 2

Sample Output

3 5

//第一个测试样例的答案为包括数字 2,8,6 的子串——在加入数字 4 并且

排序之后,它变成了数列 2,4,6,8——公差为 2 的等差数列。

【题解】

我们先将序列拆分,(a[i]%D+D)%D相等的连续一段为一组。

将每组单独处理,不同组互不影响。

先对a[i]/=D,于是我们就可以得到等式max(a[i],i{l,r})min(a[i],i{l,r})rl+kmax(a[i],i\in \{l,r\})-min(a[i],i\in \{l,r\})\le r-l+k

移项得max(a[i],i{l,r})min(a[i],i{l,r})+lr+kmax(a[i],i\in \{l,r\})-min(a[i],i\in \{l,r\})+l\le r+k

所以我们只需要枚举rr就可以了,对于ll部分可以用线段树维护。

考录如何维护max(a[i],i{l,r})min(a[i],i{l,r})max(a[i],i\in \{l,r\})-min(a[i],i\in \{l,r\})

如果加入一个数进来,会将之前所有min值比它大的数降低min-a[i],所以我们可以用单调栈维护。

max同理,最后考虑一下出现相同的情况就可以了。

【代码如下】

#include<cstdio>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=200005;
int n,K,D,Ans,AnsL,AnsR=-1,a[MAXN],b[MAXN],A[MAXN],Hsh[MAXN],Min[MAXN<<2],Add[MAXN<<2];
#include<cctype>
int read(){
	int ret=0;char ch=getchar();bool f=1;
	for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
	for(; isdigit(ch);ch=getchar()) ret=ret*10+ch-48;
	return f?ret:-ret;
}
void PushUp(int rt){Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);}
void PushDown(int rt){
	if(!Add[rt]) return;
	Add[rt<<1]+=Add[rt],Add[rt<<1|1]+=Add[rt];
	Min[rt<<1]+=Add[rt],Min[rt<<1|1]+=Add[rt],Add[rt]=0;
}
void Insert(int rt,int L,int R,int l,int r,int p){
	if(l<=L&&R<=r){Add[rt]+=p,Min[rt]+=p;return;}
	PushDown(rt);int mid=(R+L)>>1;
	if(l<=mid) Insert(rt<<1,L,mid,l,r,p);
	if(r>mid) Insert(rt<<1|1,mid+1,R,l,r,p);
	PushUp(rt);
}
int Query(int rt,int L,int R,int p){
	if(Min[rt]>p) return 0;
	if(L==R) return L;
	PushDown(rt);int mid=(R+L)>>1;
	return Min[rt<<1]<=p?Query(rt<<1,L,mid,p):Query(rt<<1|1,mid+1,R,p);
}
void Fnd(int rt,int L,int R,int l,int r,int p){
	if(l<=L&&R<=r){if(!Ans) Ans=Query(rt,L,R,p);return;}
	int mid=(R+L)>>1;PushDown(rt); 
	if(l<=mid) Fnd(rt<<1,L,mid,l,r,p);
	if(r>mid) Fnd(rt<<1|1,mid+1,R,l,r,p);
}
void Build(int rt,int L,int R){
	Add[rt]=Min[rt]=0;
	if(L==R) return;int mid=(R+L)>>1;
	Build(rt<<1,L,mid);Build(rt<<1|1,mid+1,R);
}
int s1[MAXN],Top1,s2[MAXN],Top2;
void Work(int Left){
	s1[Top1=1]=s2[Top2=1]=0;Build(1,1,*A);
	for(int i=1,lst=1;i<=*A;i++){
		for(;Top1>1&&A[s1[Top1]]<=A[i];Top1--) Insert(1,1,*A,s1[Top1-1]+1,s1[Top1],A[i]-A[s1[Top1]]);s1[++Top1]=i;
		for(;Top2>1&&A[s2[Top2]]>=A[i];Top2--) Insert(1,1,*A,s2[Top2-1]+1,s2[Top2],A[s2[Top2]]-A[i]);s2[++Top2]=i;
		Insert(1,1,*A,i,i,i);
		int L=lst=max(lst,Hsh[b[i+Left-1]]+1),R=i;Hsh[b[i+Left-1]]=i;
		Ans=0;Fnd(1,1,*A,L,R,i+K);
		if(AnsR-AnsL+1<i-Ans+1) AnsL=Left+Ans-1,AnsR=Left+i-1;
	}
	for(int i=1;i<=*A;i++) Hsh[b[i+Left-1]]=0;
}
void Updata(int x,int y){
	if(y-x<AnsR-AnsL) return;
	if(y-x>AnsR-AnsL) AnsR=y,AnsL=x;else if(x<AnsL) AnsL=x,AnsR=y;
}
int main(){
	n=read();K=read(),D=read();
	for(int i=1;i<=n;i++) Hsh[i]=a[i]=read();
	sort(Hsh+1,Hsh+1+n);int Len=unique(Hsh+1,Hsh+1+n)-Hsh-1;
	for(int i=1;i<=n;i++) b[i]=lower_bound(Hsh+1,Hsh+1+Len,a[i])-Hsh;
	memset(Hsh,0,sizeof(Hsh));
	if(D==0){
		for(int i=1,j=1;i<=n;i=j)
		for(j=i+1;j<=n&&a[j]==a[i];j++) Updata(i,j);
	}else{
		for(int l=1,r;l<=n;l=r){
			for(r=l+1;r<=n&&(a[l]%D+D)%D==(a[r]%D+D)%D;r++);*A=0;
			for(int i=l;i<r;i++) A[++*A]=a[i]/D;Work(l);
		}
	}
	printf("%d %d\n",AnsL,AnsR);
	return 0;
}
posted @ 2019-03-13 20:17  XSamsara  阅读(121)  评论(0编辑  收藏  举报