zoukankan      html  css  js  c++  java
  • BJOI2019 删数

    删数

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

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

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

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

    对于 (100\%) 的数据,

    • (1le n,m le 1.5 imes 10^5)
    • (1le a_i le n)
    • (0le ple n)
      • (p>0) 时,(1le 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(nlog 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
    ",ans.min?0:ans.cnt);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Strom在本地运行调试出现的错误
    能否通过六面照片构建3D模型?比如人脸,全身的多角度照片,生成3D模型。?
    怎么识别自己的眼型?眼型图片参照
    用opencv检测人眼并定位瞳孔位置
    仿射变换
    二维图像的三角形变换算法解释
    Labeled Faces in the Wild 人脸识别数据集
    【图像处理】计算Haar特征个数
    人脸识别技术大总结(1):Face Detection & Alignment
    基于Policy Gradient实现CartPole
  • 原文地址:https://www.cnblogs.com/autoint/p/13039696.html
Copyright © 2011-2022 走看看