bzoj4527
来源:互联网 发布:悦诗风吟雪耳面霜知乎 编辑:程序博客网 时间:2024/06/11 17:51
题意:
我们称一个数列为一个好的k-d数列,当且仅当我们在其中加上最多k个
数之后,数列排序后为一个公差为d的等差数列。
你手上有一个由n个整数组成的数列a。你的任务是找到它的最长连续子
串,使得满足子串为好的k-d数列。
1<=n<=200000;0<=k<=200000;0<=d<=10^9
#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<map>#define N 210000using namespace std;struct node{int l,r,lc,rc,d1,d2,d,lz1,lz2;}lt[2*N];struct node1{int l,r,d;}s1[N],s2[N];int n,k,d,ansl,ansr,a[N],b[N],tl,t1,t2,lim;map<int,int> ma;void updans(int l,int r){ if(r-l>ansr-ansl) ansl=l,ansr=r;}void solve1(){ int l=1; for(int i=2;i<=n;i++) { if(a[i]!=a[i-1]) l=i; updans(l,i); }}void down(int now){ int lc=lt[now].lc,rc=lt[now].rc; if(lt[now].lz1!=-1 && lt[now].lz2!=-1) { lt[now].d=lt[now].lz1-lt[now].lz2+lt[now].l; lt[now].d1=lt[now].lz1+lt[now].l; lt[now].d2=-lt[now].lz2+lt[now].l; lt[lc].lz1=lt[now].lz1;lt[lc].lz2=lt[now].lz2; lt[rc].lz1=lt[now].lz1;lt[rc].lz2=lt[now].lz2; } else if(lt[now].lz1!=-1) { lt[now].d=lt[now].d2+lt[now].lz1; lt[now].d1=lt[now].lz1+lt[now].l; lt[lc].lz1=lt[now].lz1;lt[rc].lz1=lt[now].lz1; } else if(lt[now].lz2!=-1) { lt[now].d=lt[now].d1-lt[now].lz2; lt[now].d2=-lt[now].lz2+lt[now].l; lt[lc].lz2=lt[now].lz2;lt[rc].lz2=lt[now].lz2; } lt[now].lz1=lt[now].lz2=-1;}void upd(int now){ int lc=lt[now].lc,rc=lt[now].rc; down(lc);down(rc); lt[now].d=min(lt[lc].d,lt[rc].d); lt[now].d1=min(lt[lc].d1,lt[rc].d1); lt[now].d2=min(lt[lc].d2,lt[rc].d2);}void bt(int l,int r){ int now=++tl; lt[now].l=l;lt[now].r=r;lt[now].lz1=lt[now].lz2=-1; if(l<r) { int mid=(l+r)/2; lt[now].lc=tl+1;bt(l,mid); lt[now].rc=tl+1;bt(mid+1,r); upd(now); } else lt[now].d1=lt[now].d2=lt[now].d=l;}void change(int now,int l,int r,int d,int o){ int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2; down(now); if(lt[now].l==l && lt[now].r==r) { if(o==1) lt[now].lz1=d; else lt[now].lz2=d; return; } if(mid>=r) change(lc,l,r,d,o); else if(l>mid) change(rc,l,r,d,o); else change(lc,l,mid,d,o),change(rc,mid+1,r,d,o); upd(now);}int find(int now,int l,int r){ int lc=lt[now].lc,rc=lt[now].rc,mid=(lt[now].l+lt[now].r)/2; down(now); if(lt[now].d>lim) return -1; if(lt[now].l==lt[now].r) return lt[now].l; if(mid>=r) return find(lc,l,r); else if(l>mid) return find(rc,l,r); else { int t=find(lc,l,mid); if(t==-1) t=find(rc,mid+1,r); return t; }}void solve(int st,int ed){ t1=t2=0; int pre=st; for(int i=st;i<=ed;i++) { node1 t=(node1){i,i,a[i]/d}; while(t1 && s1[t1].d<t.d) {t.l=s1[t1].l;t1--;} s1[++t1]=t; change(1,s1[t1].l,s1[t1].r,s1[t1].d,1); t=(node1){i,i,a[i]/d}; while(t2 && s2[t2].d>t.d) {t.l=s2[t2].l;t2--;} s2[++t2]=t; change(1,s2[t2].l,s2[t2].r,s2[t2].d,2); if(ma.count(a[i])) pre=max(pre,ma[a[i]]+1); lim=i+k; int l=find(1,pre,i); updans(l,i); ma[a[i]]=i; }}void solve2(){ bt(1,n); for(int i=1;i<=n;i++) b[i]=a[i]%d; int st=1; while(st<n) { int ed=st; while(ed<=n && b[ed]==b[st]) ed++; ed--; solve(st,ed); st=ed+1; }}int main(){ scanf("%d%d%d",&n,&k,&d); ansl=ansr=1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); int t=a[1]; for(int i=2;i<=n;i++) t=min(t,a[i]); for(int i=1;i<=n;i++) a[i]+=abs(t); if(d==0) solve1(); else solve2(); printf("%d %d\n",ansl,ansr); return 0;}
题解:
特判d=0
对于d!=0,模d的余数相同的子串才能作为答案。对d同余子串一个个处理。
令ai=floor(ai/d)
考虑枚举答案右端点i,那么左端点j的这个区间满足什么条件才合法?分析一下就知道是:
(max-min+1)-(i-j+1)<=k
即
max-min+j<=i+k
于是线段树维护。i右移时单调栈找出修改max和min的区间,线段树上存两个标记,维护max-min+j,max+j,-min+j最小值就可以down了。
对于i找一个最远的j也可以在线段树上找。注意询问的区间不能包含相同的数。
1 0
- bzoj4527
- bzoj4527: K-D-Sequence
- [bzoj4527]K-D-Sequence 解题报告
- 一个模块
- Leetcode: LetterCombinationsofaPhoneNumber 根据手机按键,给出所有可能的字符串
- 1110. Complete Binary Tree (25)
- 社交类APP动态详情代码通用模板
- linux系统之_中断的前世今生
- bzoj4527
- 初识jazz(IBM)
- photoshop制作圆形渐变边框教程
- EvaluateReversePolishNotation 逆波兰表达式
- Similarity Search in High Dimension via Hashing LSH 原始算法详解
- linux SVN服务器搭建并使用
- https://my.oschina.net/artshell/blog/323026
- linux系统之_虚拟文件系统的内核实现前世今生
- ViewDragHelper基本使用