CF407E k-d-sequence 数列分块或线段树+单调栈
原题:
题解:求添加k个元素后最长的等d差序列的左右端点,首先答案应该是连续的%d余数相同的一段(注意会有负数),而且这一段不可能有相同的数。对于这样的一段,将它除d化成公差为1的数列,在max{a[l-r]}-min{a[l-r]}-r<=k-l 时是等差数列。对于这样的问题考虑倒序枚举左端点,令mm[r]为i-r的最大值,mn[r]为i-r的最小值。mm[r]肯定是递增的,mn[r]肯定是递减的,考虑从l-> l-1,mm[r]显然是将小于他的一段改成a[l-1],可以用单调栈维护比他小的坐标,区间修改,mn[]也一样,用数据结构维护max{a[l-r]}-min{a[l-r]}-r,查询时要找到右符合条件的。数据结构可以用数列分块,简单,直接,好写。
需要注意的细节
1.特判 d==0 不然会re
2.注意有负数
3.判重的思想,若有r1<r2且a[r1]==a[r2],对于当前的 l 有效区间为[l,r2-1],用数较大用map维护相同的最左的坐标。
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cmath> using namespace std; const int N=2e5+10; const int M=1000000000; const int INF=1e18; int a[N],b[N],s1[N],s2[N]; int n,m,k,d,top1,top2,ansr,ansl,sum[N],pos[N],t[N],mn[N]; map<int,int>mp; void reset(int x){ mn[x]=INF; for(int i=(x-1)*m+1;i<=min(n,x*m);i++) mn[x]=min(mn[x],t[i]+sum[x]); } inline void add(int l,int r,int c){ for(int i=l;i<=min(r,pos[l]*m);i++) t[i]+=c; reset(pos[l]); if(pos[l]!=pos[r]){ for(int i=(pos[r]-1)*m+1;i<=min(n,r);i++) t[i]+=c; reset(pos[r]); } for(int i=pos[l]+1;i<=pos[r]-1;i++) sum[i]+=c,mn[i]+=c; } inline int query(int l,int r,int c){ for(int i=r;i>=max((pos[r]-1)*m+1,l);i--) if(t[i]+sum[pos[i]]<=c){ return i;} for(int i=pos[r]-1;i>=pos[l]+1;i--) if(mn[i]<=c) for(int j=i*m;j>=(i-1)*m+1;j--) if(t[j]+sum[pos[j]]<=c) {return j;} if(pos[l]!=pos[r]){ for(int i=pos[l]*m;i>=l;i--) { if(t[i]+sum[pos[i]]<=c)return i; } } } inline void sol(int L,int R){ if(L==R) return ; mp.clear();int r=R+1; top1=top2=0;s1[top1]=s2[top2]=R+1; for(int i=0;i<=pos[n];i++) sum[i]=0; for(int i=R;i>=L;i--){ if(mp.count(a[i])) r=min(r,mp[a[i]]); while(top1 && a[i]>a[s1[top1]]){ add(s1[top1],s1[top1-1]-1,-a[s1[top1]]); top1--; } s1[++top1]=i; add(s1[top1],s1[top1-1]-1,a[s1[top1]]); while(top2 && a[i]<a[s2[top2]]){ add(s2[top2],s2[top2-1]-1,a[s2[top2]]); top2--; } s2[++top2]=i; add(s2[top2],s2[top2-1]-1,-a[s2[top2]]); int rr=query(i,r-1,k-i); if(rr-i>ansr-ansl || (rr-i==ansr-ansl && i<ansl) ) ansl=i,ansr=rr; mp[a[i]]=i; } return ; } int main(){ scanf("%d%d%d",&n,&k,&d); if(d==0){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); int ansl=1;int ansr=1;int lst=1; for(int i=2;i<=n;i++){ if(a[i]!=a[lst]){ if(i-1-lst>ansr-ansl){ansl=lst;ansr=i-1;} lst=i; } } printf("%d %d ",ansl,ansr); return 0; } ansl=n+1;ansr=0;m=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=(a[i]+=M)%d;a[i]=(a[i])/d+1; pos[i]=(i-1)/m+1;t[i]=-i; } for(int i=1,j;i<=n;i=j){ for(j=i+1;j<=n;j++){ if(b[i]!=b[j]) break; } sol(i,j-1); } printf("%d %d ",ansl,ansr); return 0; }