洛谷 P5324 [BJOI2019] 删数题解
之前做模拟赛,一直没改出来这题,今天终于有时间来改一下这道题了,写一下题解纪念一下。
大致思想很简单(真的吗),题目大意就是给我们一堆数,当其中的数等于数列的长度时,就可以删除这些数。其中,它会给你两种操作,一种为单点修改,将其中的一个数改为指定值;另外一种操作则为修改整体,对其整体执行加1或减1操作。
从这个题面我们可以看出,答案与其数据顺序并无什么必然联系。所以我们可以给他放在一个桶里面去做。
我们可以先把这些数都存到桶里,然后把这个桶旋转九十度,使存值较大的点在上,然后可以观察出一个结论,答案就是未被覆盖到的数量。
证明:不想写,去看这篇题解的证明 link
接下来要解决的问题就是修改维护操作,肯定要搬出来我们高贵的线段树去维护呐。
第一个操作非常好维护,直接单修就好了。
第二个操作不大好修改,我们可以把其看为对整个序列的平移操作。相当于数列操作的正反向移动,所以找一个起点与范围,直接运动就好了。
线段树需要维护的东西:区间最小值
合并显然。
细节:st范围外无法产生贡献,要特判。
st也许小于0,建议加一个数防止下溢出。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define pi pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N=2e5+100;
int n,m;
int a[N],buc[N*3];
int lim=3*N-30,st=N-10;
struct NODE{
int mn,mncnt,ans;
};
struct SegmentTree{
NODE tr[(3*N)<<2];
int lazytag[(3*N)<<2];
#define l(a) a*2
#define r(a) a*2+1
void pushup(int p)
{
tr[p].ans=tr[l(p)].ans+tr[r(p)].ans;
tr[p].mn=min(tr[l(p)].mn,tr[r(p)].mn);
tr[p].mncnt=0;
if(tr[p].mn==tr[l(p)].mn) tr[p].mncnt+=tr[l(p)].mncnt;
if(tr[p].mn==tr[r(p)].mn) tr[p].mncnt+=tr[r(p)].mncnt;
}
void pushdown(int p)
{
if(lazytag[p])
{
tr[l(p)].mn+=lazytag[p],tr[r(p)].mn+=lazytag[p];
lazytag[l(p)]+=lazytag[p],lazytag[r(p)]+=lazytag[p];
if(!tr[l(p)].mn) tr[l(p)].ans=tr[l(p)].mncnt;
else tr[l(p)].ans=0;
if(!tr[r(p)].mn) tr[r(p)].ans=tr[r(p)].mncnt;
else tr[r(p)].ans=0;
lazytag[p]=0;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
tr[p]={0,1,1};
return;
}
int mid=l+r>>1;
build(l(p),l,mid),build(r(p),mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int x,int y,int v)
{
if(x<=l && r<=y)
{
tr[p].mn+=v,lazytag[p]+=v;
if(!tr[p].mn) tr[p].ans=tr[p].mncnt;
else tr[p].ans=0;
return;
}
int mid=l+r>>1;
pushdown(p);
if(mid>=x) change(l(p),l,mid,x,y,v);
if(mid<y) change(r(p),mid+1,r,x,y,v);
pushup(p);
}
int ask(int p,int l,int r,int x,int y)
{
if(x<=l && r<=y) return tr[p].ans;
int mid=l+r>>1,res=0;
pushdown(p);
if(mid>=x) res+=ask(l(p),l,mid,x,y);
if(mid<y) res+=ask(r(p),mid+1,r,x,y);
return res;
}
}Tr;
signed main()
{
freopen("delete.in","r",stdin);
freopen("delete.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]+=st,buc[a[i]]++;
}
Tr.build(1,1,lim);
for(int i=st+1;i<=st+n;i++)
if(buc[i])
Tr.change(1,1,lim,i-buc[i]+1,i,1);
while(m--)
{
int p,x;
cin>>p>>x;
if(p)
{
if(a[p]<=st+n) Tr.change(1,1,lim,a[p]-buc[a[p]]+1,a[p]-buc[a[p]]+1,-1);
buc[a[p]]--;
a[p]=st+x;
if(a[p]<=st+n) Tr.change(1,1,lim,a[p]-buc[a[p]],a[p]-buc[a[p]],1);
buc[a[p]]++;
}
else
{
if(x==1)
{
if(buc[st+n])
Tr.change(1,1,lim,st+n-buc[st+n]+1,st+n,-1);
st--;
}
else
{
st++;
if(buc[st+n])
Tr.change(1,1,lim,st+n-buc[st+n]+1,st+n,1);
}
}
cout<<Tr.ask(1,1,lim,st+1,st+n)<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!