原题:http://codeforces.com/contest/407/problem/E
题解:求添加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\n",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\n",ansl,ansr);
return 0;
}