[BJOI2019] 删数

https://www.luogu.org/problemnew/show/P5324

题解

首先我们需要弄清这个答案是什么。

对于一个长度为n的序列,那么它先删的肯定是\(n\),删完之后它就会跳到\(n-cnt[n]\)位置,然后变成子问题继续做 。

于是我们把每个数看做一条覆盖\(n-cnt[n]+1 \sim n\)的一条线段,那么有解的前提是\(1\sim n\)中的每个数都被覆盖了。

如果没有,需要调整多少次呢?

可以发现,我们可以花费一的代价将一条线段的长度-1,再将另一条线段长度+1,可以发现答案就是所有没有被覆盖的位置的长度和。

然后用线段树完成这个操作,整体加的话就将询问区间平移,注意:右端点不在询问区间内的线段要清掉。

代码

#include<bits/stdc++.h>
#define N 150009
#define P pair<int,int>
#define mm make_pair
using namespace std;
typedef long long ll;
int tr[N*12],la[N*12],num[N*12],nowl,nowr,maxn,n,m,a[N],tag;
map<int,int>tong;
inline ll rd(){
	ll x=0;char c=getchar();bool f=0;
	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
void build(int cnt,int l,int r){
	num[cnt]=r-l+1;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r); 
}
inline void pushdown(int cnt){
	la[cnt<<1]+=la[cnt];
	tr[cnt<<1]+=la[cnt];
	la[cnt<<1|1]+=la[cnt];
	tr[cnt<<1|1]+=la[cnt];
	la[cnt]=0;
}
inline P merge(P x,P y){
	P z=x;
	if(y.first<z.first)z=y;
	else if(y.first==z.first)z.second+=y.second;
	return z;
}
inline void pushup(int cnt){
	tr[cnt]=tr[cnt<<1];num[cnt]=num[cnt<<1];
	if(tr[cnt<<1|1]<tr[cnt])tr[cnt]=tr[cnt<<1|1],num[cnt]=num[cnt<<1|1];
	else if(tr[cnt<<1|1]==tr[cnt])num[cnt]+=num[cnt<<1|1];
}
P query(int cnt,int l,int r,int L,int R){
	if(l>=L&&r<=R)return mm(tr[cnt],num[cnt]);
	int mid=(l+r)>>1;
	if(la[cnt])pushdown(cnt);
	if(mid>=L&&mid<R)return merge(query(cnt<<1,l,mid,L,R),query(cnt<<1|1,mid+1,r,L,R));
	if(mid>=L)return query(cnt<<1,l,mid,L,R);
	if(mid<R)return query(cnt<<1|1,mid+1,r,L,R);
}
void upd(int cnt,int l,int r,int L,int R,int tag){
	if(l>=L&&r<=R){
		tr[cnt]+=tag; 
	    la[cnt]+=tag;
	    return;
	}
	int mid=(l+r)>>1;
	if(la[cnt])pushdown(cnt);
	if(mid>=L)upd(cnt<<1,l,mid,L,R,tag);
	if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,tag);
	pushup(cnt);
}
inline void work(int l,int r,int tag){
	l=max(l,nowl);r=min(r,nowr);
	if(l>r)return; 
	upd(1,1,maxn,l-nowl,r-nowl,tag);
}
int main(){
	n=rd();m=rd();
	nowl=1-m-1;nowr=n+m+1;
	maxn=nowr-nowl+1;
	build(1,1,maxn);
	int ls=1,rs=n;
	for(int i=1;i<=n;++i)a[i]=rd(),tong[a[i]]++;
	for(int i=1;i<=n;++i)work(i-tong[i]+1,i,1);
	int p,x;
	while(m--){
		p=rd();x=rd();
		if(!p){
			ls-=x;rs-=x;tag-=x;
			if(x<0){
				int xx=rs,yy=ls-1;
				if(tong.find(xx)!=tong.end())work(xx-tong[xx]+1,xx,1);
				if(tong.find(yy)!=tong.end())work(yy-tong[yy]+1,yy,-1);
			}
			else{
				int xx=ls,yy=rs+1;
				if(tong.find(xx)!=tong.end())work(xx-tong[xx]+1,xx,1);
				if(tong.find(yy)!=tong.end())work(yy-tong[yy]+1,yy,-1);
			}
		}
		else{
			x+=tag;
			if(a[p]>=ls&&a[p]<=rs)work(a[p]-tong[a[p]]+1,a[p]-tong[a[p]]+1,-1);
			tong[a[p]]--;
			a[p]=x;
			tong[a[p]]++;
			if(a[p]>=ls&&a[p]<=rs)work(a[p]-tong[a[p]]+1,a[p]-tong[a[p]]+1,1);
		}
		P xx=query(1,1,maxn,ls-nowl,rs-nowl);
		if(xx.first==0)printf("%d\n",xx.second);
		else puts("0");
	}
	return 0;
}
posted @ 2019-04-28 11:56  comld  阅读(364)  评论(0编辑  收藏  举报