洛谷 P5324 [BJOI2019] 删数题解

link

之前做模拟赛,一直没改出来这题,今天终于有时间来改一下这道题了,写一下题解纪念一下。

大致思想很简单(真的吗),题目大意就是给我们一堆数,当其中的数等于数列的长度时,就可以删除这些数。其中,它会给你两种操作,一种为单点修改,将其中的一个数改为指定值;另外一种操作则为修改整体,对其整体执行加1或减1操作。

从这个题面我们可以看出,答案与其数据顺序并无什么必然联系。所以我们可以给他放在一个桶里面去做。
我们可以先把这些数都存到桶里,然后把这个桶旋转九十度,使存值较大的点在上,然后可以观察出一个结论,答案就是未被覆盖到的数量。

证明:不想写,去看这篇题解的证明 link

接下来要解决的问题就是修改维护操作,肯定要搬出来我们高贵的线段树去维护呐。

第一个操作非常好维护,直接单修就好了。

第二个操作不大好修改,我们可以把其看为对整个序列的平移操作。相当于数列操作的正反向移动,所以找一个起点与范围,直接运动就好了。

线段树需要维护的东西:区间最小值 mn,区间最小值数量 mncnt,答案 ans,以及懒标记 lazytag

合并显然。

细节: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;
} 
posted @   袍蚤  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示