原题传送门

易知这个数列的顺序是不用考虑的

我们看两个数列 \(1,2,3\)和\(3,3,3\)都能删完,再看两个数列\(1,2,3,4\)和\(2,2,4,4\),也都能删完

不难发现,我们珂以把这些数字塞进桶中,记\(cnt_i\)表示数字\(i\)出现的次数,对于每个\(i\),在一颗线段树上把区间\([i-cnt_i+1,i]\)赋值成1(因为一次删\(cnt_i\)个珂以转化成每次删\(1\)个,值从大向小递减),最后看[1,n]上有几个点不是1,这就是题目所求的答案

单点修改就直接在线段树上单点修改,区间加减实际就相当于线段树值域平移,但这个实在太麻烦,相对的,我们珂以平移查询区间

我们珂以一开始就把\(1\)设为\(Max(n,m)+1\)这样就不用考虑负数的问题了

时间复杂度是\(O(m\log (2*Max(n,m)+n))\)

假·完整代码(这个是假算法)

#include <bits/stdc++.h>
#define N 450005
#define M 150005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int n,m,a[M],lim,cnt[N],w;
int tr[N<<3],sum[N];
inline void modify(register int x,register int l,register int r,register int pos,register int val)
{
if(l==r)
{
tr[x]=val;
return;
}
int mid=l+r>>1;
if(pos<=mid)
modify(x<<1,l,mid,pos,val);
else
modify(x<<1|1,mid+1,r,pos,val);
tr[x]=tr[x<<1]+tr[x<<1|1];
}
inline int query(register int x,register int l,register int r,register int L,register int R)
{
if(L<=l&&r<=R)
return tr[x];
int mid=l+r>>1,res=0;
if(L<=mid)
res+=query(x<<1,l,mid,L,R);
if(R>mid)
res+=query(x<<1|1,mid+1,r,L,R);
return res;
}
int main()
{
n=read(),m=read();
lim=m+n*2;
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
for(register int i=1;i<=n;++i)
{
a[i]=read();
if((++sum[n+a[i]-cnt[a[i]+m]])==1)
modify(1,1,lim,n+a[i]-cnt[a[i]+m],1);
++cnt[a[i]+m];
}
for(register int i=1;i<=m;++i)
{
int opt=read(),x=read();
if(opt)
{
x-=w;
--cnt[a[opt]+m];
if((--sum[n+a[opt]-cnt[a[opt]+m]])==0)
modify(1,1,lim,n+a[opt]-cnt[a[opt]+m],0);
a[opt]=x;
if((++sum[n+a[opt]-cnt[a[opt]+m]])==1)
modify(1,1,lim,n+a[opt]-cnt[a[opt]+m],1);
++cnt[a[opt]+m];
}
else
w+=x;
write(n-query(1,1,lim,n+1-w,n+n-w)),puts("");
}
return 0;
}

交一发,发现会WA46

实际因为我们有种情况没有考虑:当\(val>n\)时,所有的都要修改,然而到线段树上就变成了一段区间,会对答案造成影响

我们只需要动态插入/删除区间即可,这样线段树要维护区间最小值及其个数

真·完整代码

#include <bits/stdc++.h>
#define N 450005
#define M 150005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline int Max(register int a,register int b)
{
return a>b?a:b;
}
int n,m,a[M],b[N],cnt[N],c,ql,qr,lim;
int minn[N<<3],sum[N<<3],tag[N<<3];
inline void pushup(register int x)
{
int ls=x<<1,rs=x<<1|1;
minn[x]=minn[ls],sum[x]=sum[ls];
if(minn[rs]<minn[x])
minn[x]=minn[rs],sum[x]=sum[rs];
else if(minn[rs]==minn[x])
sum[x]+=sum[rs];
}
inline void build(register int x,register int l,register int r)
{
if(l==r)
{
minn[x]=b[l];
sum[x]=1;
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
inline void pushdown(register int x)
{
if(tag[x])
{
int ls=x<<1,rs=x<<1|1;
tag[ls]+=tag[x],tag[rs]+=tag[x];
minn[ls]+=tag[x],minn[rs]+=tag[x];
tag[x]=0;
}
}
inline void modify(register int x,register int l,register int r,register int L,register int R,register int val)
{
if(L<=l&&r<=R)
{
minn[x]+=val;
tag[x]+=val;
return;
}
int mid=l+r>>1;
pushdown(x);
if(L<=mid)
modify(x<<1,l,mid,L,R,val);
if(R>mid)
modify(x<<1|1,mid+1,r,L,R,val);
pushup(x);
}
inline int query(register int x,register int l,register int r,register int L,register int R)
{
if(L<=l&&r<=R)
return minn[x]?0:sum[x];
int mid=l+r>>1,res=0;
pushdown(x);
if(L<=mid)
res+=query(x<<1,l,mid,L,R);
if(R>mid)
res+=query(x<<1|1,mid+1,r,L,R);
return res;
}
int main()
{
n=read(),m=read();
c=Max(n,m);
for(register int i=1;i<=n;++i)
{
a[i]=read();
++cnt[a[i]+=c];
}
ql=c+1,qr=c+n,lim=c*2+n;
for(register int i=m+1;i<=qr;++i)
++b[i-cnt[i]+1],--b[i+1];
for(register int i=2;i<=qr+1;++i)
b[i]+=b[i-1];
build(1,1,lim);
for(register int i=1;i<=m;++i)
{
int opt=read(),x=read();
if(opt)
{
--cnt[a[opt]];
if(a[opt]<=qr)
modify(1,1,lim,a[opt]-cnt[a[opt]],a[opt]-cnt[a[opt]],-1);
a[opt]=x+ql-1;
modify(1,1,lim,a[opt]-cnt[a[opt]],a[opt]-cnt[a[opt]],1);
++cnt[a[opt]];
}
else
{
if(x==1)
{
if(cnt[qr])
modify(1,1,lim,qr-cnt[qr]+1,qr,-1);
--ql,--qr;
}
else
{
++ql,++qr;
if(cnt[qr])
modify(1,1,lim,qr-cnt[qr]+1,qr,1);
}
}
write(query(1,1,lim,ql,qr)),puts("");
}
return 0;
}

【题解】Luogu P5324 [BJOI2019]删数的更多相关文章

  1. luogu P5324 [BJOI2019]删数

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

  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. 【LG2839】[国家集训队]middle

    [LG2839][国家集训队]middle 题面 洛谷 题解 按照求中位数的套路,我们二分答案\(mid\),将大于等于\(mid\)的数设为\(1\),否则为\(-1\). 若一个区间和大于等于\( ...

  2. JMeter压测工具安装及使用总结

    一.安装 进入apache官网https://www.apache.org/dist/jmeter/binaries下载Windows版本JMeter: 二.配置环境变量 下载之后解压,配置环境变量 ...

  3. 复旦高等代数I(19级)每周一题

    本学期的高等代数每周一题活动计划从第2教学周开始,到第15教学周结束,每周的周末公布一道思考题(共14道,思考题一般与下周授课内容密切相关),供大家思考和解答.每周一题将通过“高等代数官方博客”(以博 ...

  4. Spring不能直接@autowired注入Static变量

    一.业务场景 spring框架应用中有些静态方法需要依赖被容器管理的类,就像这样: @Component public class Test { @Autowired private static U ...

  5. 记一次phpmyadmin 4.8.1 远程文件包含漏洞(BUUCTF web)

    题目很简单,一个滑稽 打开源码,发现存在source.php文件 于是访问文件,发现出现一串php源码 提示存在hint.php,于是访问发现一句话 flag not here, and flag i ...

  6. 【Gamma】Scrum Meeting 1

    目录 写在前面 gamma阶段角色更换 进度情况 任务进度表 Gamma阶段燃尽图 遇到的困难 照片 代码Commit记录 后端 前端 写在前面 例会时间:5.25 22:30-23:00 例会地点: ...

  7. Python导入 from lxml import etree 导入不了

    问题在学爬虫,Python 版本是2.7,安装的lxml包是4.3的,在 from lxml import etree 时发现一直报错,网上查询,原来是Python版本和lxml包版本不一致导致的. ...

  8. Learning to Track Any Object

    Learning to Track Any Object 2019-10-28 12:14:49 Paper: https://arxiv.org/abs/1910.11844 1.

  9. Netty 4 实现一个 NettyClient

    本文章为作者原创,有问题的地方请提出指正. 1.类继承Diagram 2.定义EndPoint类 目前仅仅定义了2个方法,分别用来获取本地或远程服务器的地址. package netty; impor ...

  10. .netcore里使用StackExchange.Redis TimeOut 情况解决方法

    在用StackExchange.Redis这个组件时候,时不时会出现异常TimeOut解决方法如下, 解决方法: 在Program的Main入口方法里添加一句话: System.Threading.T ...