【BJOI2019】删数(贪心)(线段树)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zxyoi_dreamer/article/details/102667188

传送门


题解:

话说 BJOI2019 Day2 稍微难点的就这一道啊,而且这道题也不算特别难的。

那BJ今年省选Day1爆炸的岂不是翻盘无望

首先考虑一个正确性显然的贪心。

我们把所有数放到数轴上,重复的叠起来,然后把 [ 1 , n ] [1,n] 中的柱子(宽度忽略不计)向左边推倒。没有覆盖的位置总数就是答案。

于是我们维护一个全局位移标记,线段树维护每个点覆盖次数,区间 0 0 的个数。修改就很好处理了。

需要注意的是我们并不能把 n n 之后的柱子推倒,所以在区间右端点滑动的时候需要考虑更新,左端点不用,因为左端点滑过去的时候一定是把这个柱子扔出区间外的时候,不会影响询问。所以线段树还要支持区间加,由于覆盖次数一定是非负,维护区间最小值出现次数即可维护区间 0 0 的个数。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;bool f=0;
		while(!isdigit(c=gc()))f=c=='-';num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

inline int min(int a,int b){return a<b?a:b;}
cs int N=1.5e5+7,M=4e6+7;
int n,m,b,ps,nn,a[N],ct[M];

#define lc u<<1
#define rc u<<1|1
int mn[M],tim[M];
inline void pushup(int u,int tag){
	mn[u]=std::min(mn[lc],mn[rc]);
	tim[u]=(mn[u]==mn[lc]?tim[lc]:0)+(mn[u]==mn[rc]?tim[rc]:0);
	mn[u]+=tag;
}
inline void build(int u,int l,int r){
	if(l==r){mn[u]=0,tim[u]=1;return;}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	pushup(u,0);
}
inline void update(int u,int l,int r,int p,int w){
	if(l==r){mn[u]+=w;return ;}
	int mid=l+r>>1,tag=mn[u]-min(mn[lc],mn[rc]);
	(p<=mid)?update(lc,l,mid,p,w):update(rc,mid+1,r,p,w);
	pushup(u,tag);
}
inline void modify(int u,int l,int r,int ql,int qr,int w){
	if(ql<=l&&r<=qr){mn[u]+=w;return ;}
	int mid=l+r>>1,tag=mn[u]-min(mn[lc],mn[rc]);
	if(ql<=mid)modify(lc,l,mid,ql,qr,w);
	if(mid<qr)modify(rc,mid+1,r,ql,qr,w);
	pushup(u,tag);
}
inline int query(int u,int l,int r,int ql,int qr,int ad=0){
	if(ql<=l&&r<=qr)return mn[u]+ad==0?tim[u]:0;
	int mid=l+r>>1,tag=mn[u]-min(mn[lc],mn[rc]);
	if(qr<=mid)return query(lc,l,mid,ql,qr,tag+ad);
	if(mid<ql)return query(rc,mid+1,r,ql,qr,tag+ad);
	return query(lc,l,mid,ql,qr,tag+ad)+query(rc,mid+1,r,ql,qr,tag+ad);
}
#undef lc 
#undef rc
inline void ins(int x){
	int p=x-(ct[x]++);
	if(x<=ps+n)update(1,1,nn,p,1);
}
inline void del(int x){
	int p=x-(--ct[x]);
	if(x<=ps+n)update(1,1,nn,p,-1);
}

signed main(){
#ifdef zxyoi
	freopen("number.in","r",stdin);
#endif
	n=gi(),m=gi(),nn=(n+m)<<1;b=ps=n+m;build(1,1,nn);
	for(int re i=1;i<=n;++i)ins(a[i]=gi()+b);
	while(m--){
		int p=gi(),x=gi();
		if(p)del(a[p]),ins(a[p]=ps+x);
		else {
			if(x==-1){++ps;if(ct[ps+n])x=ps+n,modify(1,1,nn,x-ct[x]+1,x,1);}
			else {if(ct[ps+n])x=ps+n,modify(1,1,nn,x-ct[x]+1,x,-1);--ps;}
		}
		cout<<query(1,1,nn,ps+1,ps+n)<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/102667188