Codeforces 题面传送门 & 洛谷题面传送门

深感自己线段树学得不扎实……

首先特判掉 \(d=0\) 的情况,显然这种情况下满足条件的区间 \([l,r]\) 中的数必须相同,双针扫一遍即可。

接下来考虑如何解决 \(d\ne 0\) 的情况。碰到这样的问题我们肯定首先要把区间合法的充要条件一一罗列出来,不难发现由于我们的过程只有加数,没有删数,因此原序列中两两数之差也必须是 \(d\) 的倍数,也即区间中所有数模 \(d\) 同余,又显然区间中两两数必须互不相同,因此我们考虑令 \(b_i=\lfloor\dfrac{a_i}{d}\rfloor,c_i=a_i\bmod d\),那么前面两个条件即可翻译为:

  • \(\forall i\in[l,r],c_i=c_l\)
  • \(b_l,b_{l+1},b_{l+2},\cdots,b_r\) 互不相同

接下来考虑最多加入 \(k\) 个数这个条件。显然经过我们这么一转化,最终形成的序列的 \(b\) 值必须形成公差恰好为 \(1\) 的等差数列。而如果我们记 \(L=\min\limits_{i=l}^rb_i,R=\max\limits_{i=l}^rb_i\),那么我们肯定不会加入 \(b\) 值在 \([L,R]\) 以外的数,因此我们加入数的个数的最小值就是 \((R-L+1)-(r-l+1)=(R-L)-(r-l)\),因此我们还可以得到条件:

  • \((R-L)-(r-l)\le k\)

考虑怎么维护这个东西,这东西显然不好分治对吧,那我们就扫描线求解,枚举右端点,维护可行的左端点的集合。假设右端点扫描到 \(r\),那么显然满足前两个条件的 \(l\) 肯定会形成一段区间 \([L_l,R_l]\),且显然有 \(R_l=r\)。那么对于第一个条件,如果我们扫描到某个 \(r\) 满足 \(c_r\ne c_{r-1}\),就令 \(L_l=r\),对于第二个条件,我们在扫描的过程中维护 \(pre_i\) 表示上一个 \(b_j=i\) 的位置,然后每扫到一个 \(r\) 就令 \(L_l\) 对 \(pre_{b_r}+1\) 取 \(\max\) 即可。比较棘手的是第三个条件,不过这东西是可以单调栈+线段树维护的,具体维护方法参加 CF997E,因此考虑单调栈维护一波这个东西,这样我们要求的就是 \([L_l,R_l]\) 中第一个 \(\le k\) 的位置,这显然可以线段树二分在 \(\log n\) 的时间内求出(而我甚至 \(\log^2n\) 的做法都没想到,说明 5448),总复杂度 \(n\log n\)。

const int MAXN=2e5;
int n,k,d,a[MAXN+5],b[MAXN+5],c[MAXN+5];
map<int,int> pre;
int getmod(int x,int v){return (x%v+v)%v;}
int getdiv(int x,int v){return (x-getmod(x,v))/v;}
struct node{int l,r,mn,lz;} s[MAXN*4+5];
void pushup(int k){s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void tag(int x,int v){s[x].mn+=v;s[x].lz+=v;}
void pushdown(int k){if(s[k].lz) tag(k<<1,s[k].lz),tag(k<<1|1,s[k].lz),s[k].lz=0;}
void modify(int k,int l,int r,int v){
if(l<=s[k].l&&s[k].r<=r) return tag(k,v),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,v);else if(l>mid) modify(k<<1|1,l,r,v);
else modify(k<<1,l,mid,v),modify(k<<1|1,mid+1,r,v);
pushup(k);
}
int findleq(int k,int l,int r,int v){
if(s[k].mn>v) return -1;
if(l<=s[k].l&&s[k].r<=r){
if(s[k].l==s[k].r) return s[k].l;
pushdown(k);int ps,mid=s[k].l+s[k].r>>1;
if(~(ps=findleq(k<<1,l,mid,v))) return ps;
return findleq(k<<1|1,mid+1,r,v);
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return findleq(k<<1,l,r,v);
else if(l>mid) return findleq(k<<1|1,l,r,v);
else{
int ps;
if(~(ps=findleq(k<<1,l,mid,v))) return ps;
return findleq(k<<1|1,mid+1,r,v);
}
}
int main(){
scanf("%d%d%d",&n,&k,&d);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(!d){
pii res=mp(0,0);
for(int l=1,r;l<=n;l++){
r=l;while(r<=n&&a[l]==a[r]) ++r;
chkmax(res,mp(r-l,-l));
} printf("%d %d\n",-res.se,-res.se+res.fi-1);
return 0;
} build(1,1,n);pii res;
for(int i=1;i<=n;i++) b[i]=getdiv(a[i],d),c[i]=getmod(a[i],d);
stack<int> stk_mn,stk_mx;stk_mn.push(0);stk_mx.push(0);
for(int i=1,mnl=1;i<=n;i++){//(mx-mn)-(r-l)<=k
if(c[i]!=c[i-1]) chkmax(mnl,i);
chkmax(mnl,pre[b[i]]+1);pre[b[i]]=i;
while(stk_mn.size()>1&&b[i]<b[stk_mn.top()]){
int p=stk_mn.top();stk_mn.pop();
modify(1,stk_mn.top()+1,p,b[p]-b[i]);
// printf("modify %d %d %d\n",stk_mn.top()+1,p,b[p]-b[i]);
} while(stk_mx.size()>1&&b[i]>b[stk_mx.top()]){
int p=stk_mx.top();stk_mx.pop();
modify(1,stk_mx.top()+1,p,-b[p]+b[i]);
// printf("modify %d %d %d\n",stk_mx.top()+1,p,-b[p]+b[i]);
} if(i^1) modify(1,1,i-1,-1)/*,printf("modify %d %d %d\n",1,i-1,-1)*/;
stk_mn.push(i);stk_mx.push(i);
int ps=findleq(1,mnl,i,k);
if(~ps) chkmax(res,mp(i-ps+1,-ps));
} printf("%d %d\n",-res.se,-res.se+res.fi-1);
return 0;
}

Codeforces 407E - k-d-sequence(单调栈+扫描线+线段树)的更多相关文章

  1. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

  2. 51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

     区间计数   基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80   两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下: Ans:=Σni=1Σnj=i[max{ ...

  3. 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)

    线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...

  4. 【XSY2720】区间第k小 整体二分 可持久化线段树

    题目描述 给你你个序列,每次求区间第\(k\)小的数. 本题中,如果一个数在询问区间中出现了超过\(w\)次,那么就把这个数视为\(n\). 强制在线. \(n\leq 100000,a_i<n ...

  5. HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  6. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  7. HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)

    Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...

  8. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  9. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

随机推荐

  1. Kali安装OWASP

    我是2019版的kali,里面并没有自带OWASP工具,因为OWASP不再更新的因素,所以新版kali将它移除了  安装OWASP apt-get install zaproxy #以下都是安装软件时 ...

  2. 剑指offer:JZ12 矩阵中的路径

    JZ12 矩阵中的路径 描述 请设计一个函数,用来判断在一个n乘m的矩阵中是否存在一条包含某长度为len的字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上, ...

  3. MySQL:提高笔记-2

    MySQL:提高笔记-2 学完基础的语法后,进一步对 MySQL 进行学习,第一篇为:MySQL:提高笔记-1,这是第二篇内容 说明:这是根据 bilibili 上 黑马程序员 的课程 mysql入门 ...

  4. Java中类及方法的加载顺序

    1. 虚拟机在首次加载Java类时,会对静态代码块.静态成员变量.静态方法进行一次初始化(静态间按顺序执行). 2. 只有在调用new方法时才会创建类的实例. 3. 类实例创建过程:父子继承关系,先父 ...

  5. 6月2日 Scrum Meeting

    日期:2021年6月2日 会议主要内容概述: 取消账单类别自定义 图表属性分析取消函数输入 增加新的主题模板 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇 ...

  6. Django+Vue跨域配置与经验

    一.原理 同源?同源策略? 同源的定义是:两个页面的协议.端口和域名都相同 同源的例子: 不同源的例子: 同源策略SOP(Same origin policy)是一种浏览器约定,它是浏览器最核心也最基 ...

  7. Sharding-JDBC自定义复合分片算法

    Sharding-JDBC自定义复合分片算法 一.背景 二.需求 1.对于客户端操作而言 2.对于运营端操作而言 三.分片算法 1.客户id和订单id的生成规则 2. 确定数据落在那个表中 3.举例说 ...

  8. Ajax配合后端实现Excel的导出

    一.需求 在我们的日常开发中,可能经常需要遇到excel的导出,以往excel的导出服务器端都是使用的 GET 方法,但是某些情况下,服务器端只能使用 POST 方法,那么我们有没有好的方法实现exc ...

  9. elasticsearch的bulk(批量)操作

    在es中我们可能会有这么一种需求,即有时需要批量向es中插入或更新或删除数据,如果一条一条数据的操作,那么速度必然很慢,那么es的bulk api就可以派上用场. delete 删除操作,只需要写一个 ...

  10. STM32必学的时钟系统

    STM32的时钟系统 相较于51单片机,stm32的时钟系统可以说是非常复杂了,我们现在看下面的一张图:   上图说明了时钟的走向,是从左至右的从时钟源一步步的分配给外设时钟.需要注意的是,上图左侧一 ...