• 线段树维护区间前k小


    线段树维护区间前k小



    $ solution: $

    觉得超级钢琴太麻烦?在这里线段树提供一条龙服务

    咳咳,开始讲正题!这道题我们有一个和超级钢琴复杂度一样 $ ~O(~sum x imes logn)~ $ 的做法。因为线段数支持动态维护最小值,而取 $ max $ 操作我们可以用线段树的 $ lazytag $ 实现(不懂可以看看代码里的标记下传和区间修改)。所以我们主要目的就是输出区间前 $ x $ 小,这个其实我们可以用线段树的单点修改完成!

    我们在区间 $ [l,r] $ 里面找最小值,假设其下标为 $ k $ 我们记录它的位置和它的权值。然后我们暂时将这个位置在线段树上用单点修改操作改成 $ inf $ ,这样我们就不会再将这个位置作为最小值。然后我们再在 $ [l,r] $ 中找一个最小值,这时我们可以找到另一个位置 $ k_2 $ ,然后重复给它变成 $ inf $ 的操作,并记录它的位置和权值。这样不断循环,当我们找完所有的区间前 $ x $ 小后。我们再用线段树的单点修改操作,将线段树上对应位置的值改回来!然后这题就做完了!

    注意每一个最小值我们都要花 $ log $ 的时间查找,更改为 $ inf $ ,最后再改回来。 复杂度中有一定常数,但是算法只用了线段树,没有其它数据结构鱼龙混杂,跑起来效果还不错,当然重点是码量小一些!



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define rg register int
    #define zuo k<<1,l,mid
    #define you k<<1|1,mid+1,r
    #define midd int mid=(l+r)>>1
    #define pushd push(k,k<<1,k<<1|1)
    
    using namespace std;
    
    int n,m;
    int x,y,v,t,sx,sv;
    int as[500005];
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    	while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    	if(sign)return -res; else return res;
    }
    
    struct su{
    	int da,id;
    	inline void min(const su a){
    		if(a.da<da){da=a.da;id=a.id;}
    	}
    }a[500005];
    
    struct tree{
    	int da[500005<<4];
    	int lz[500005<<4];
    	inline void build(int k,int l,int r){ //建树
    		if(l==r){da[k]=qr(); return ;}
    		midd; build(zuo); build(you);
    		da[k]=min(da[k<<1],da[k<<1|1]);
    	}
    
    	inline void push(int k,int l,int r){ //下传标记
    		if(lz[k]>da[l]){
    			da[l]=max(da[l],lz[k]);
    			lz[l]=max(lz[l],lz[k]);
    		}
    		if(lz[k]>da[r]){
    			da[r]=max(da[r],lz[k]);
    			lz[r]=max(lz[r],lz[k]);
    		} lz[k]=0;
    	}
    
    	inline void add1(int k,int l,int r){ //区间取max
    		if(x<=l&&r<=y){
    			if(da[k]>=v)return ; //权值只增不降
    			da[k]=max(da[k],v);
    			lz[k]=max(lz[k],v);
    			return ;
    		}if(lz[k])pushd; midd;
    		if(x<=mid)add1(zuo);
    		if(y>mid) add1(you);
    		da[k]=min(da[k<<1],da[k<<1|1]);
    	}
    
    	inline void add2(int k,int l,int r){ //单点修改权值
    		if(l==r){da[k]=sv; return ;}
    		if(lz[k])pushd; midd;
    		if(sx<=mid) add2(zuo);
    		else add2(you);
    		da[k]=min(da[k<<1],da[k<<1|1]);
    	}
    
    	inline su ask(int k,int l,int r){ //询问区间里的最小值信息
    		if(l==r)return su{da[k],l};
    		if(lz[k])pushd; midd;
    		if(x<=l&&r<=y){ //注意我们要精确的找到最小值位置
    			if(da[k<<1]==da[k])return ask(zuo);
    			else return ask(you);
    		}
    		register su res; res.da=1e9; res.id=1001;
    		if(x<=mid)res=ask(zuo);
    		if(y>mid) res.min(ask(you));
    		return res;
    	}
    }tr;
    
    int main(){
    	n=qr(); tr.build(1,1,n); m=qr();
    	for(rg i=1;i<=m;++i){
    		rg f=qr(); x=qr(); y=qr(); v=qr();
    		if(f==1){ //区间修改
    			tr.add1(1,1,n);
    		} else{
    			t=qr(); //
    			for(rg j=1;j<=t;++j){
    				a[j]=tr.ask(1,1,n); //读取最小值位置及权值
    				if(a[j].da>=v){t=j;break;} //不符合题意
    				sx=a[j].id; sv=1e9; //让最小值消失
    				tr.add2(1,1,n); //让之前的最小值不再被选中
    			}
    			if(a[t].da>=v) printf("-1");
    			else for(rg j=1;j<=t;++j) printf("%d ",a[j].da);
    			for(rg j=1;j<=t;++j){
    				sx=a[j].id; sv=a[j].da; //将之前改的变回原值
    				tr.add2(1,1,n);
    			}puts("");
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    MySQL5.7 容器化安装
    什么是架构?——软件系统架构的定义
    服务端高并发分布式架构演进之路(转)
    CentOS7 增加回环地址
    三句话看明白jdk收费吗
    【转载】C#里怎么把string类型转换成double
    【转载】如何查看自己网站的搜索引擎收录量和索引量
    【转载】c# datatable 判断值是否存在
    【转载】C#中Datatable修改列名
    【转载】C#使用typeof运算符获取对象变量的具体类型Type
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11270833.html
Copyright © 2020-2023  润新知