• 洛谷P1712 [NOI2016]区间 尺取法+线段树+离散化


    洛谷P1712 [NOI2016]区间

    noi2016第一题(大概是签到题吧,可我还是不会)


    链接在这里

    题面可以看链接;

    先看题意

    这么大的l,r,先来个离散化

    很容易,我们可以想到一个结论

    假设一个点被覆盖次数大于m

    我们将覆盖这个点的区间升序排序;

    则所选区间一定是排序后序列中的一个长度为m+1的连续子序列

    证明很容易,取更远的点会使最大值更大从而使差值最大

    我们可以从这个结论出发,再观察该题所求,符合尺取法的思路

    我们考虑用尺取法求解

    没了解尺取法的读者可以去自行了解一下

    如何求解呢?

    我们考虑将区间按权值大小升序排序

    从小到大加载到数轴上,统计数轴上点被覆盖的最大次数

    当我们将一个区间加载后若被覆盖的最大次数大于m则说明存在符合条件的点

    我们区间最大上界已经确定,接着确定下界

    将区间由加入顺序向后删除

    当删除一个区间后总体max的值要小于m

    区间序列的下界便确定了,

    目前便得到了有可能更新答案的区间序列的最大值和最小值

    在此我对几个点进行解释

    **1 .首先我们为什么要按权值排序,

    原因便是我们一开始就证明过的性质

    利用该性质我们可以得到可能更新答案的所有情况从而求解

    2 .最大值与最小值之前的区间呢?不会影响答案吗?

    不会影响,我们关心的只是符合题意的区间最小值和最大值

    只关注边界,至于内部在所选序列中的区间具体是谁我们并不关心

    3.我们在确定区间序列下界时将一些区间删掉了

    不会对结果有影响吗?

    事实上,我们删掉的区间一定是对答案无贡献的

    证明很容易

    我们删除区间的大小一定小于目前正在寻找的下界

    即使之后在加入某个大区间时这个区间产生了贡献成为最小区间

    但所加入的最大区间一定大于等于之前的上界

    而该区间又小于之前的下界

    所以差值一定大于先前的值,故不对最终答案贡献,

    有了这些思路后我们就可以做了

    至于如何获得当前数轴的最大覆盖次数

    和如何将区间加入数轴

    我们维护一颗最大值的线段树即可

    注意因为我们采取了离散化

    所以线段树数组的大小由4倍变为8倍**

    时间复杂度便是线段树的时间复杂度了

    显然是可以过5e5数据的

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    #include<algorithm>
    #define inf 0x3f3f3f3f
    using namespace std;
    const int maxn =5e5+1;
    int tree[maxn*8];
    int add[maxn*8];
    inline int read(){
    	int ret=0;
    	int f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-'){
    			f=-f;
    		}
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9'){
    		ret=ret*10+(ch^'0');
    		ch=getchar();
    	}
    	return f*ret;
    }
    int n,m;
    struct edge{
    	int val;
    	int num;
    }e[maxn];
    struct node{
    	int val;
    	int num;
    }p[maxn*2];
    int cnt;
    bool cmp(node x,node y){
    	return x.val<y.val;
    }
    bool cmp2(edge x,edge y){
    	return x.val<y.val;
    }
    int ln[maxn*2];
    int rn[maxn*2];
    void pushdown(int rt){
    	if(add[rt]){
    		int ls=rt*2;
    		int rs=rt*2+1;
    		tree[ls]+=add[rt];
    		tree[rs]+=add[rt];
    		add[ls]+=add[rt];
    		add[rs]+=add[rt];
    		add[rt]=0;
    	}
    	return ;
    }
    void update(int ro,int l,int r,int ls,int rs,int val){
    	if(rs<l||ls>r){
    		return ;
    	}
    	if(rs>=r&&ls<=l){
    		tree[ro]+=val;
    		add[ro]+=val;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	pushdown(ro);
    	update(ro*2,l,mid,ls,rs,val);
    	update(ro*2+1,mid+1,r,ls,rs,val);
    	tree[ro]=max(tree[ro*2],tree[ro*2+1]);//维护区间最大点值 
    	return ;
    }
    int main(){
    //	freopen("a.in","r",stdin);
    	n=read();
    	m=read();
    	int l,r;
    	for(int i=1;i<=n;i++){
    		l=read();
    		r=read();
    	//	cout<<l<<" "<<r<<endl;
    		e[i].num=i;
    		e[i].val=r-l;
    		cnt++;
    		p[cnt].num=i;
    		p[cnt].val=l;
    		cnt++;
    		p[cnt].num=i;
    		p[cnt].val=r;
    	}
    	int num=0;
    	sort(p+1,p+1+cnt,cmp);
    	for(int i=1;i<=cnt;i++){
    		if(p[i].val!=p[i-1].val){
    			num++;//num为新建的权值,当相邻值相等时,权值不变 
    		}
    		int u=p[i].num;//更新原本的L,与r; 
    		if(!ln[u]){
    			ln[u]=num;
    		}
    		else rn[u]=num;
    	}
    	sort(e+1,e+1+n,cmp2);
    	int rig=num;
    	int ri=0;
    	int li=0;
    	int ans=inf;
    	/*
    	本题该部分采用了尺取法
    	当区间内没有点被覆盖次数大于m时,我们加入一个区间进去
    	如果加入此区间  
    	//cout<<m;
    	*/
    	while(true){
    		while(tree[1]<m&&ri<n){//尺取法,当我们加入一个节点,如果此事有一点被覆盖次数大于等于m,这个区间为合法最大值 
    			ri++;
    			int u=e[ri].num;
    			int ls=ln[u];
    			int rs=rn[u];
    			update(1,1,rig,ls,rs,1);
    		}
    		if(tree[1]<m){
    			break;//我们即使将所有区间加入也无法大于m故放弃 
    		}
    		while(tree[1]>=m&&li<n){
    			li++;
    			int u=e[li].num;
    			int ls=ln[u];
    			int rs=rn[u];
    			update(1,1,rig,ls,rs,-1);
    		}//寻找影响这个区间的最小值 
    		ans=min(ans,e[ri].val-e[li].val);// 更新最小值 
    	}
    	if(ans==inf){
    		cout<<-1<<endl;
    		return 0;
    	}
    	cout<<ans;
    	return 0;
    }
    

    完结撒花


  • 相关阅读:
    SSH和SSL比较
    SSL虚拟主机安全方案
    https在电子邮件安全解决方案
    centos tomcat安装
    laravel 添加第三方扩展库
    laravel-1 安装.配置
    centos7.0 vsftp配置
    centos 日常操作指令
    centos redis 安装
    centos php 扩展安装
  • 原文地址:https://www.cnblogs.com/rpup/p/13642824.html
Copyright © 2020-2023  润新知