题目

题目大意

给你一个数列,让你找到一个最长的连续子序列,满足在添加了至多KKK个数之后,能够变成一条公差为DDD的等差数列。


思考历程

一眼看上去似乎是一道神题……

没有怎么花时间思考,毕竟时间都砸到T1和T2上了。


正解

仔细推一下就会发现这种等差数列[j,i][j,i][j,i]有三个简单的条件:

  1. 所有数模ddd的余数相同
  2. 没有重复的数(d=0d=0d=0的时候除外)
  3. max(j,i)−min(j,i)d+1≤i−j+1+k\frac{max(j,i)-min(j,i)}{d}+1\leq i-j+1+kdmax(j,i)−min(j,i)​+1≤i−j+1+k

前面两个都能很快地预处理。

现在我们枚举一个右边界rrr,前面两个条件可以使我们得知最远的左边界LLL。

我们把第三个条件的式子乱搞一下:max(l,r)−min(l,r)−(r−l)d≤kdmax(l,r)-min(l,r)-(r-l)d\leq kdmax(l,r)−min(l,r)−(r−l)d≤kd

考虑一下能不能维护左边的式子。最重要的是维护最小值和最大值。

随便想想就可以知道,左边界lll在区间[L,r][L,r][L,r]中,最小值的取值是一段一段的(最大值也一样)。

或许能够用这条性质来维护。

接着就可以想到单调队列。对于最小值和最大值分别维护一条单调队列。

设单调队列上某个位置的值是xxx,上一个位置是yyy,那么区间[y+1,x][y+1,x][y+1,x]中的值为a[x]a[x]a[x]。

于是我们可以在线段树上区间修改。

在维护单调队列的过程中,最小(大)值会有些变化。这也可以直接在线段树上改。

至于−(r−l)d-(r-l)d−(r−l)d,这个是很容易维护的,每次直接将区间[1,r−1][1,r-1][1,r−1]减ddd就可以了。

询问的时候就是寻找线段树上最左边的小于kdkdkd的值。这个可以维护最小值,在线段树上二分就可以维护了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define N 200010
#define INF 1000000000000000000
int n;
long long K,D;
int a[N];
bool bz[N];
map<int,int> last;
int fr[N];
int q1[N],q2[N];//min max
int h1,t1,h2,t2;
long long t[N*4],lazy[N*4];
inline void pushdown(int k){
t[k<<1]+=lazy[k],t[k<<1|1]+=lazy[k];
lazy[k<<1]+=lazy[k],lazy[k<<1|1]+=lazy[k];
lazy[k]=0;
}
void build(int k,int l,int r){
t[k]=INF;
if (l==r)
return;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void add(int k,int l,int r,int st,int en,long long c){
if (st<=l && r<=en){
t[k]+=c;
lazy[k]+=c;
return;
}
pushdown(k);
int mid=l+r>>1;
if (st<=mid)
add(k<<1,l,mid,st,en,c);
if (mid<en)
add(k<<1|1,mid+1,r,st,en,c);
t[k]=min(t[k<<1],t[k<<1|1]);
}
void change0(int k,int l,int r,int x){
if (l==r){
t[k]=0;
return;
}
pushdown(k);
int mid=l+r>>1;
if (x<=mid)
change0(k<<1,l,mid,x);
else
change0(k<<1|1,mid+1,r,x);
t[k]=min(t[k<<1],t[k<<1|1]);
}
int query(int k,int l,int r,int en){
if (t[k]>K*D)
return 0;
if (l==r)
return l;
pushdown(k);
int mid=l+r>>1,res=0;
res=query(k<<1,l,mid,en);
if (!res && mid<en)
res=query(k<<1|1,mid+1,r,en);
return res;
}
int main(){
scanf("%d%lld%lld",&n,&K,&D);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
if (D==0){
int ansl=1,len=1;
for (int i=1,j=0;i<=n;++i)
if (a[i]!=a[i-1]){
if (i-j>len)
len=i-j,ansl=j;
else if (i-j==len)
ansl=j;
}
printf("%d %d\n",ansl,ansl+len-1);
return 0;
}
for (int i=1;i<=n;++i)
if (i==1 || (a[i]%D+D)%D!=(a[i-1]%D+D)%D)
bz[i]=1;
for (int i=1;i<=n;++i){
fr[i]=last[a[i]];
last[a[i]]=i;
}
build(1,1,n);
h1=h2=1,t1=t2=0;
int ansl=1,len=1;
for (int i=1,st=1;i<=n;++i){
if (bz[i]){
h1=h2=1,t1=t2=0;
if (st<=i-1)
add(1,1,n,st,i-1,INF);
st=i;
}
else if (st<=fr[i]){
add(1,1,n,st,fr[i],INF);
st=fr[i]+1;
}
while (h1<=t1 && q1[h1]<st)
h1++;
while (h2<=t2 && q2[h2]<st)
h2++;
while (h1<=t1 && a[q1[t1]]>=a[i]){
add(1,1,n,q1[t1-1]+1,q1[t1],+a[q1[t1]]-a[i]);
t1--;
}
while (h2<=t2 && a[q2[t2]]<=a[i]){
add(1,1,n,q2[t2-1]+1,q2[t2],-a[q2[t2]]+a[i]);
t2--;
}
q1[++t1]=q2[++t2]=i;
if (st<=i-1)
add(1,1,n,st,i-1,-D);
change0(1,1,n,i);
int l=query(1,1,n,i);
if (i-l+1>len)
len=i-l+1,ansl=l;
else if (i-l+1==len && l<ansl)
ansl=l;
}
printf("%d %d\n",ansl,ansl+len-1);
return 0;
}

总结

单调队列可以干的东西,可不仅仅是DP啊……

[JZOJ4665] 【GDOI2017模拟7.21】数列的更多相关文章

  1. 【JZOJ4793】【GDOI2017模拟9.21】妮厨的愤怒

    题目描述 栋栋和标标都是厨力++的妮厨.俗话说"一机房不容二厨",他们两个都加入了某OI( )交流♂( )群,在钦定老婆的时候出现了偏差,于是闹得不可开交.可是栋栋是群内的长者,斗 ...

  2. jzoj4915. 【GDOI2017模拟12.9】最长不下降子序列 (数列)

    题面 题解 调了好几个小时啊--话说我考试的时候脑子里到底在想啥-- 首先,这个数列肯定是有循环节的,而且循环节的长度\(T\)不会超过\(D\) 那么就可以把数列分成三份,\(L+S+R\),其中\ ...

  3. 【JZOJ5086】【GDOI2017第四轮模拟day1】数列 折半搜索

    题面 有一个长度为n 的排列,现在有一些位置的数已经模糊不清了,你只知道这个排列的逆序对个数是K,你能计算出总共有多少可能的排列吗? 对于100% 的数据,n <=10^3,K<=10^9 ...

  4. jzoj4918. 【GDOI2017模拟12.9】最近公共祖先 (树链剖分+线段树)

    题面 题解 首先,点变黑的过程是不可逆的,黑化了就再也洗不白了 其次,对于\(v\)的祖先\(rt\),\(rt\)能用来更新答案当且仅当\(sz_{rt}>sz_{x}\),其中\(sz\)表 ...

  5. jzoj4916. 【GDOI2017模拟12.9】完全背包问题 (背包+最短路)

    题面 题解 考场上蠢了--这么简单的东西都想不到-- 首先排序加去重. 先来考虑一下,形如 \[a_1x_1+a_2x_2+...a_nx_n=w,a_1<a_2<...<a_n,x ...

  6. NOIP模拟赛 某种数列问题

    众所周知,chenzeyu97有无数的妹子(阿掉!>_<),而且他还有很多恶趣味的问题,继上次纠结于一排妹子的排法以后,今天他有非(chi)常(bao)认(cheng)真(zhe)去研究一 ...

  7. [考试反思]0814NOIP模拟测试21

    前两名是外校的240.220.kx和skyh拿到了190的[暴力打满]的好成绩. 我第5是170分,然而160分就是第19了. 在前一晚上刚刚爆炸完毕后,心态格外平稳. 想想前一天晚上的挣扎: 啊啊啊 ...

  8. [JZOJ4633] 【GDOI2017模拟7.15】萌萌哒

    题目 描述 题目大意 给你一个数列,接下来有许多个操作,使得区间[l1,r1][l_1,r_1][l1​,r1​]和[l2,r2][l_2,r_2][l2​,r2​]对应的位置染上同样的颜色(使得它们 ...

  9. 2019.8.14 NOIP模拟测试21 反思总结

    模拟测试20的还没改完先咕着 各种细节问题=错失190pts T1大约三分钟搞出了式子,迅速码完,T2写了一半的时候怕最后被卡评测滚去交了,然后右端点没有初始化为n…但是这样还有80pts,而我后来还 ...

随机推荐

  1. 《深入理解Java虚拟机》- 重载与重写

    这一节打算从“方法调用”的主题进行分析. 方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不设计方法内部的具体运行过程. 一.概念 解析调用:所有 ...

  2. WAMP中的mysql设置密码(默认密码为空)及phpmyadmin的配置

    来自:  http://wenku.baidu.com/link?url=J4K28e1kt-_ykJLsOtS1b5T6hKj5IzL5hXSKIiB133AvPCUXLlxGKScsBsxi0mA ...

  3. extern static和函数

    #include <stdio.h> int sum(int a, int b); int main() { /************************************** ...

  4. PHP操作XML方法之SimpleXML

    SimpleXML简介 SimpleXML 扩展提供了一个非常简单和易于使用的工具集,能将XML转换成一个带有一般属性选择器和数组迭代器的对象. 举例XML XML结构部分引用自<<深入理 ...

  5. 2019-4-15-VisualStudio-如何在-NuGet-包里面同时包含-DEBUG-和-RELEASE-的库

    title author date CreateTime categories VisualStudio 如何在 NuGet 包里面同时包含 DEBUG 和 RELEASE 的库 lindexi 20 ...

  6. 16_TLB与流水线

    1 前面做的实验起始有缺陷 访问内存之后,后面执行两句代码后:并不能保证刚才访问的代码还在TLB中:有可能被刷新出去了: 实验验证缺陷: 代码 不连续 TLB 被淘汰: 2万次中有1次被淘汰:由于访问 ...

  7. 关于while循环中的break和continue的区别

    while循环如果不加条件限制的话,它会一直循环下去,那么问题就来了,如果我不用条件去终止while循环的话,那么我该用什么方法去终止呢? 你可以选择两种终止while循环的方法 1.break  强 ...

  8. selenium+plantomJS

    #!/usr/bin/env python # -*- coding:utf-8 -*- """ 流程框架: 1.搜索关键词,利用selenium驱动浏览器搜索关键词,查 ...

  9. 8.Struts2拦截器

    1. 拦截器的概述 * 拦截器就是AOP(Aspect-Oriented Programming)的一种实现.(AOP是指用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.) * ...

  10. layui中load具体用法

    遮盖窗体,demo: layer.load(,{ // content: "加载中...", shade: [0.4,'#000'], //0.1透明度的白色背景 time:* } ...