BZOJ4527: K-D-Sequence【线段树+单调栈】
4527: K-D-Sequence
Description
我们称一个数列为一个好的k-d数列,当且仅当我们在其中加上最多k个数之后,数列排序后为一个公差为d的等差数列。
你手上有一个由n个整数组成的数列a。你的任务是找到它的最长连续子串,使得满足子串为好的k-d数列。
Input
第一行包含三个用空格隔开的整数。
第二行包含n个空格隔开的整数:表示数列a。
Output
输出两个用空格隔开的整数,表示数列是好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,于是我们就可以得到等式
移项得
所以我们只需要枚举就可以了,对于部分可以用线段树维护。
考录如何维护
如果加入一个数进来,会将之前所有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;
}