ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BJOI2019 删数

2020-06-03 19:57:19  阅读:298  来源: 互联网

标签:BJOI2019 cnt 数列 min int 删数 tag calc


删数

对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:

  • 记当前数列长度为 \(k\),则删掉数列中所有等于 \(k\) 的数。

现有一个长度为 \(n\) 的数列 \(a\),有 \(m\) 次修改操作,第 \(i\) 次修改后你要回答:经过 \(i\) 次修改后的数列 \(a\),至少还需要修改几个数才可删空?

每次修改操作为单点修改或数列整体加一或数列整体减一。

对于 \(100\%\) 的数据,

  • \(1\le n,m \le 1.5 \times 10^5\)
  • \(1\le a_i \le n\)
  • \(0\le p\le n\)
    • \(p>0\) 时,\(1\le x \le n\)。
    • \(p=0\) 时,\(x=-1\) 或 \(1\)。

题解

http://jklover.hs-blog.cf/2020/05/29/Loj-3094-删数/

线段树.

考虑对于给定的数列,如何计算最小修改次数.

若当前数列长度为 \(x\) ,则操作一次后会转移到 \(x-cnt(x)\) ,其中 \(cnt(x)\) 表示数列中有几个 \(x\) .

想让数列长度变为 \(0​\) ,这相当于每个长度都要被删去一次,而 \(x​\) 可以让 \([x-cnt(x)+1,x]​\) 这些长度都被删一次.

于是 \([1,n]\) 内每种数字 \(x\) 可以覆盖一段区间 \([x-cnt(x)+1,x]\) ,询问 \([1,n]\) 内有几个位置没被覆盖即为答案.

可以用线段树维护每个位置被覆盖的次数,询问最小值以及最小值出现的次数即可得到答案.

现在考虑如何支持修改,单点修改可以直接删掉原来的线段,加上新的线段.

整体 \(+1,-1\) 可以直接对全局维护偏移量标记 \(\Delta\) ,询问 \([1,n]\) 时改为询问 \([1-\Delta,n-\Delta]\) 即可.

注意整体 \(+1,-1\) 时,可能会导致 \(n\) 与 \(n+1\) 交换,此时需要修改它们的贡献.

时间复杂度 \(O(n\log n)\) .

CO int N=4.5e5+10;
int a[N],cnt[N];

struct node {int min,cnt;} tree[4*N];
int tag[4*N];

IN node operator+(CO node&a,CO node&b){
	node c={min(a.min,b.min)};
	if(a.min==c.min) c.cnt+=a.cnt;
	if(b.min==c.min) c.cnt+=b.cnt;
	return c;
}

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void build(int x,int l,int r){
	tree[x]={0,r-l+1},tag[x]=0;
	if(l==r) return;
	build(lc,l,mid),build(rc,mid+1,r);
}
IN void put_tag(int x,int v){
	tree[x].min+=v,tag[x]+=v;
}
IN void push_down(int x){
	if(tag[x]){
		put_tag(lc,tag[x]),put_tag(rc,tag[x]);
		tag[x]=0;
	}
}
void modify(int x,int l,int r,int ql,int qr,int v){
	if(qr<l or ql>r) return;
	if(ql<=l and r<=qr) return put_tag(x,v);
	push_down(x);
	if(ql<=mid) modify(lc,l,mid,ql,qr,v);
	if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
	tree[x]=tree[lc]+tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return tree[x];
	push_down(x);
	if(qr<=mid) return query(lc,l,mid,ql,qr);
	if(ql>mid) return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}

int main(){
	int n=read<int>(),m=read<int>(),mx=n+m+m;
	build(1,1,mx);
	for(int i=1;i<=n;++i){
		a[i]=read<int>()+m;
		++cnt[a[i]];
		modify(1,1,mx,a[i]-cnt[a[i]]+1,a[i]-cnt[a[i]]+1,1);
	}
	int delta=0;
	function<int(int)> calc=[&](int x){
		return x+m-delta;
	};
	for(int i=1;i<=m;++i){
		int p=read<int>();
		if(!p){
			int x=read<int>();
			if(x==1){
				if(cnt[calc(n)])
					modify(1,1,mx,calc(n)-cnt[calc(n)]+1,calc(n),-1);
			}
			else{
				if(cnt[calc(n+1)])
					modify(1,1,mx,calc(n+1)-cnt[calc(n+1)]+1,calc(n+1),1);
			}
			delta+=x;
		}
		else{
			int x=read<int>();
			if(a[p]<=calc(n))
				modify(1,1,mx,a[p]-cnt[a[p]]+1,a[p]-cnt[a[p]]+1,-1);
			--cnt[a[p]];
			a[p]=x+m-delta;
			++cnt[a[p]];
			if(a[p]<=calc(n))
				modify(1,1,mx,a[p]-cnt[a[p]]+1,a[p]-cnt[a[p]]+1,1);
		}
		node ans=query(1,1,mx,calc(1),calc(n));
		printf("%d\n",ans.min?0:ans.cnt);
	}
	return 0;
}

标签:BJOI2019,cnt,数列,min,int,删数,tag,calc
来源: https://www.cnblogs.com/autoint/p/13039696.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有