传送门

不如先考虑暴力,能删的序列首先有\(1,2,3...n\),还有就是升序排序后从后往前放数,第\(i\)位要么放\(i\),要么放\(i+1\)位置的数,例如\(1,2,4,4,5,6,9,9,9\)

如果一个数\(i\)出现了若干次,假如是\(num_i\)次,我们发现是可以在\(i,i-1,i-2...i-num_i+1\)上放\(i\)的,这样放完之后,如果有的位置没有用到现有的数放上去,那么就要从没用的数里改一个放过来,问题也就是用一堆数,最多能放多少个位置.考虑从后往前放,然后如果一个位置有多个数就接着用这种数往后放.如果放到一个位置\(j\),\(j\)有若干个,当前的\(i\)也有若干个,我们只能用一种放,那么显然要用更多的那种放.最后没放数的位置数就是答案.

为了方便,我们把放数看成区间覆盖,即一个\(i\)可以覆盖\([i-num_i+1,i]\)这段区间,我们再给所有覆盖区间加上\(1\),那就是求\([1,n]\)中\(0\)的个数.注意到修改的\(+1/-1\)都是整体的,所以如果把所有数放在一根数轴上,那么初始要统计的区间为\([1,n]\),然后整体\(+1\)相当于统计区间整体向左移\(1\),然后 以原统计区间右端点 为右端点 的对应区间覆盖的贡献要减掉,因为那个右端点的数超过统计范围,不能放进来;整体\(-1\)相当于统计区间整体向右移\(1\),然后 以原统计区间右端点 为右端点 的对应区间覆盖的贡献要加上;单点修改,也就是原来的\(num_{a_p}\)减\(1\),后面的新的\(num\)加\(1\),这导致对应两个覆盖区间左端点右移和左移.这些东西可以用线段树维护,注意线段树的叶子数量应该是\(n+q+q\)

至于区间\(0\)的数量,用值域线段树维护因为区间\(+1\),某个位置最少为\(0\),只要维护区间最小值及数量就行了

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double using namespace std;
const int N=150000+10,M=N*3;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
struct node
{
int mi,nm;
node(){mi=0,nm=1;}
node(int nmi,int nnm){mi=nmi,nm=nnm;}
node operator + (const node &bb) const
{
if(mi==bb.mi) return node(mi,nm+bb.nm);
return mi<bb.mi?(*this):bb;
}
void ad(int x){mi+=x;}
}s[M<<2],nw;
int tg[M<<2];
void psup(int o){s[o]=s[o<<1]+s[o<<1|1];}
void psdn(int o){if(tg[o]) s[o<<1].ad(tg[o]),tg[o<<1]+=tg[o],s[o<<1|1].ad(tg[o]),tg[o<<1|1]+=tg[o],tg[o]=0;}
#define mid ((l+r)>>1)
int nl,nr;
void modif(int o,int l,int r,int ll,int rr,int x)
{
if(ll<=l&&r<=rr){s[o].ad(x),tg[o]+=x;return;}
psdn(o);
if(ll<=mid) modif(o<<1,l,mid,ll,rr,x);
if(rr>mid) modif(o<<1|1,mid+1,r,ll,rr,x);
psup(o);
}
void modif(int lx,int x)
{
int o=1,l=nl,r=nr,st[21],tp=0;
while(l<r)
{
st[++tp]=o,psdn(o);
if(lx<=mid) o=o<<1,r=mid;
else o=o<<1|1,l=mid+1;
}
s[o].ad(x);
while(tp)
{
o=st[tp--];
psup(o);
}
}
node quer(int o,int l,int r,int ll,int rr)
{
if(ll<=l&&r<=rr) return s[o];
psdn(o);
node an;
an.mi=M;
if(ll<=mid) an=an+quer(o<<1,l,mid,ll,rr);
if(rr>mid) an=an+quer(o<<1|1,mid+1,r,ll,rr);
psup(o);
return an;
}
void bui(int o,int l,int r)
{
if(l==r) return;
bui(o<<1,l,mid),bui(o<<1|1,mid+1,r);
psup(o);
}
int n,q,a[N],nm[M],ll=150001,rr; int main()
{
n=rd(),q=rd();
rr=ll+n-1;
nl=ll-q,nr=rr+q;
bui(1,nl,nr);
for(int i=1;i<=n;++i)
{
a[i]=rd()+ll-1;
++nm[a[i]];
modif(a[i]-nm[a[i]]+1,1);
}
while(q--)
{
int p=rd();
if(!p)
{
if(~rd())
{
if(nm[rr]) modif(1,nl,nr,rr-nm[rr]+1,rr,-1);
--ll,--rr;
}
else
{
++ll,++rr;
if(nm[rr]) modif(1,nl,nr,rr-nm[rr]+1,rr,1);
}
}
else
{
if(a[p]<=rr) modif(a[p]-nm[a[p]]+1,-1);
--nm[a[p]];
a[p]=rd()+ll-1;
++nm[a[p]];
modif(a[p]-nm[a[p]]+1,1);
}
nw=quer(1,nl,nr,ll,rr);
printf("%d\n",nw.mi?0:nw.nm);
}
return 0;
}

luogu P5324 [BJOI2019]删数的更多相关文章

  1. 【题解】Luogu P5324 [BJOI2019]删数

    原题传送门 易知这个数列的顺序是不用考虑的 我们看两个数列 \(1,2,3\)和\(3,3,3\)都能删完,再看两个数列\(1,2,3,4\)和\(2,2,4,4\),也都能删完 不难发现,我们珂以把 ...

  2. [BJOI2019]删数(线段树)

    [BJOI2019]删数(线段树) 题面 洛谷 题解 按照值域我们把每个数的出现次数画成一根根的柱子,然后把柱子向左推导,\([1,n]\)中未被覆盖的区间长度就是答案. 于是问题变成了单点修改值,即 ...

  3. 题解 洛谷 P5324 【[BJOI2019]删数】

    先考虑对于一个序列,能使其可以删空的的修改次数. 首先可以发现,序列的排列顺序是没有影响的,所以可以将所有数放到桶里来处理. 尝试对一个没有经过修改的可以删空的序列来进行删数,一开始删去所有的\(n\ ...

  4. [BJOI2019] 删数

    https://www.luogu.org/problemnew/show/P5324 题解 首先我们需要弄清这个答案是什么. 对于一个长度为n的序列,那么它先删的肯定是\(n\),删完之后它就会跳到 ...

  5. [BJOI2019] 删数 [dp转贪心结论+线段树]

    题面 传送门 思路 dp部分 以下称合法序列为原题面中可以删空的序列 这个是我在模拟考场上的思路 一开始我是觉得,这个首先可以写成一个dp的形式:$dp[i][j]$表示用$j$个数字填满了目标序列的 ...

  6. Luogu P2426 【删数】

    状态定义: 一眼区间$DP$,从左右两边删不好定义状态,不如定义$dp[i][j]$表示$[i,j]$未删的最大值,转移就很自然了 转移: 从左边删$dp[i][j]=max(dp[i][j],dp[ ...

  7. Luogu5324 BJOI2019删数(线段树)

    考虑无修改怎么做.对于1~n的每个数,若其存在,将最后一个放在其值的位置,剩余在其前面依次排列,答案即为值域1~n上没有数的位置个数.带修改显然记一下偏移量线段树改一改就好了. #include< ...

  8. [Luogu5324][BJOI2019]删数(线段树)

    CF风格题,先猜结论,记数列中i这个数共出现了cnt[i]次,那么所有区间[i-cnt[i]+1,i]的并集的补集大小就是答案. 于是我们只需要线段树维护每个位置是否被某个区间覆盖到即可,对于整体加减 ...

  9. 【LOJ】#3094. 「BJOI2019」删数

    LOJ#3094. 「BJOI2019」删数 之前做atcoder做到过这个结论结果我忘了... em,就是\([1,n]\)之间每个数\(i\),然后\([i - cnt[i] + 1,i]\)可以 ...

随机推荐

  1. C#二维码与条形码的生成

    二维码 using Gma.QrCodeNet.Encoding;using Gma.QrCodeNet.Encoding.Windows.Render; string str = "Htt ...

  2. Selenium 4即将发布:每个QA都应该知道的

    阅读原文 Simon Stewart(Selenium的创始成员)在班加罗尔的Selenium大会议上正式确认了Selenium4.0的发布日期和一些主要更新.我们先来提前了解一下Selenium 4 ...

  3. HTML基础知识个人总结

    [学习的网站是主要是W3school,还加上一些其他搜索学习到的内容,仅在博客做个人整理] 一.标签——尖括号围成的关键词,成对出现. ※使用时必须符合标签嵌套规则 1. (1) <!DOCTY ...

  4. 团队开发项目--NABCD模型

    N(need)需求: 鉴于在学校中的大部分爱学习的学生平时都去拍空教室的占有情况,我们发现有的时候太多,导致同学们们拍照会浪费很长的时间,而且空教室的显示不是一下子全出来,有的时候还会出现无法显示的情 ...

  5. Spring 完成自动注入(autowire)

    目录 两个测试类 普通方式手动注入 普通方式注入的缺点 自动注入的介绍 配置自动注入的方式 配置全局自动注入 局部单独配置 利用注解实现自动注入 两个测试类 package cn.ganlixin.p ...

  6. 注意:QQ影音视频压缩时长丢失

    客户宣传片发来,高清的,比较大,500多M,需要转成小一点的,放在客户网站上,于是用QQ影音转码压缩下,变成低质量的.如下 一切都很顺利,提示进度100%! 这一切都是电脑自动的,又是提示成功的,千想 ...

  7. java基础问题巩固(1)

    你对java垃圾回收了解吗?什么时候需要使用? 答: 垃圾回收器的作用是查找和回收(清理)无用的对象,从而让jvm更 有效的使用内存.但是运行因为垃圾回收本身会有开销,过于频繁的使用会导致性能下降.比 ...

  8. Linux(Ubuntu)使用日记------部署JavaWeb项目到服务器

    0.前言 本博文内容是建立在你可以通过SSH连接到远程服务器的基础上的,如果你还没有用SSH连接到远程服务器,请参考此文(腾讯云服务器): http://www.cnblogs.com/hwtblog ...

  9. 【转】C语言堆栈入门——堆和栈的区别

    @2019-04-25 [小记] C语言堆栈入门——堆和栈的区别

  10. react 生命周期函数介绍

    constructor():构造函数 执行:组件加载钱最先调用一次,仅调用一次. 作用:定义状态机变量. 注意:第一个语句必须为super(), 否则会报错:'this' is not allowed ...