zoukankan      html  css  js  c++  java
  • 区间(贪心做法+线段树优化)

    题目

    这道题目的话,按(r)从小到大排序,然后如果对于当前的区间没有满足至少(c_i)个的话,就尽量选右边的没有选过的数字。显而易见是正确的

    正确性证明:

    对于当前这个区间(i)而言,如果前面(i-1)个区间已经是用了最少的数字,且尽量的把数字凑到(i)区间的(l)的的话,如果想要让前(i-1)的区间对(i)区间的贡献(+1),那么前(i-1)个区间必须多用一个数字。

    那么我们证明,全部放右边能让前(i)个区间也满足这样的性质。(其实就是数学归纳法)


    首先证明最少,首先前面是已经尽量的靠右边放了,也就是说对于这个区间的贡献是已经最大化了,那么由于其是最小了,所以我们不能让其更小,那么我们就只能求让其贡献变大来减少我们在这个区间放的数字个数,但是由于贡献增加数和区间少放的数字呈(1:1)关系,所以并不会让总数字减少。

    那么我们继续证明贡献是最大化的情况,由于按(r)排序,所以对于第(i+1)区间交集部分而言,假设交集部分是([x,r]),如果这个区间的数字都被选了,那么就是最大化的情况,如果没有放满,说明我们可以多放一个数字增加贡献,但是这样就违背了数字总个数最小的法则,而且前(i-1)个区间也是最小的,并不能给第(i)个区间提供一个放数字的可能,所以我们并不能增加贡献,同时如果要增加一个贡献,那么也要让总数字加(1)

    当然,有可能第(i+1)个区间还和第(y)个区间有交集,然后让前(y)个区间总个数(+1),然后贡献(+1),重点是由于(r)从小到大排,所以这个贡献是有可能作用于第(i)个区间的,这样不就让贡献减一的同时让个数(+1)了吗?但是由于这个第(i)个区间接收到了贡献,少放了一个数字,且这个数字的位置一定大于等于,第(y)个区间增加贡献所放的数字位置,这样这个少放的数字也会作用于第(i+1)个区间,所以其实是没有增加贡献的,且由于数字前移了,对于后面的区间所能做的贡献可能会减少,是极其不优秀的。

    所以第(i)个区间同样满足此性质,证毕。在这里插入图片描述

    而且不难证明,第一个区间对于第二个区间就满足这样的性质,数学归纳法完成。


    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define  N  51000
    #define  NN  110000
    using  namespace  std;
    inline  int  mymin(int  x,int  y){return  x<y?x:y;}
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    struct  node
    {
    	int  l,r,c,lazy,d;
    }tr[NN];int  len;
    void  bt(int  l,int  r)
    {
    	int  now=++len;tr[now].d=r-l+1;
    	if(l<r)
    	{
    		int  mid=(l+r)>>1;
    		tr[now].l=len+1;bt(l,mid);
    		tr[now].r=len+1;bt(mid+1,r);
    	}
    }
    inline  void  updata(int  x){tr[x].c=tr[tr[x].l].c+tr[tr[x].r].c;}
    inline  void  get_lazy(int  x){tr[x].c=tr[x].d;tr[x].lazy=1;}
    inline  void  downdata(int  x)
    {
    	if(tr[x].lazy)get_lazy(tr[x].l),get_lazy(tr[x].r),tr[x].lazy=0;
    }
    int  change(int  now,int  l,int  r,int  ll,int  rr,int  k)//返回值为改变了 
    {
    	int  mid=(l+r)>>1,lc=tr[now].l,rc=tr[now].r;
    	downdata(now);
    	if(l==ll  &&  r==rr)
    	{
    		int  val=0;
    		if(tr[now].d-tr[now].c<=k)val=tr[now].d-tr[now].c,get_lazy(now);
    		else
    		{
    			val=change(rc,mid+1,r,mid+1,rr,k);
    			if(val<k)change(lc,l,mid,ll,mid,k-val);
    			updata(now);
    			val=k;
    		}
    		return  val;
    	}
    	int  val=0;
    	if(rr<=mid)val=change(lc,l,mid,ll,rr,k);
    	else  if(mid<ll)val=change(rc,mid+1,r,ll,rr,k);
    	else
    	{
    		val=change(rc,mid+1,r,mid+1,rr,k);
    		if(val<k)val+=change(lc,l,mid,ll,mid,k-val);
    	}
    	updata(now);
    	return  val;
    }
    int  findans(int  now,int  l,int  r,int  ll,int  rr)
    {
    	if(l==ll  &&  r==rr)return  tr[now].c;
    	downdata(now);
    	int  mid=(l+r)>>1,lc=tr[now].l,rc=tr[now].r;
    	if(rr<=mid)return  findans(lc,l,mid,ll,rr);
    	else  if(mid<ll)return  findans(rc,mid+1,r,ll,rr);
    	else  return  findans(lc,l,mid,ll,mid)+findans(rc,mid+1,r,mid+1,rr);
    }
    int  n;
    struct  CHANGE
    {
    	int  x,y,z;
    }ch[N];int  floor_limit=999999999,ceil_limit;
    inline  bool  cmp(CHANGE  x,CHANGE  y){return  x.y<y.y;}
    int  main()
    {
    	scanf("%d",&n);
    	for(int  i=1;i<=n;i++)
    	{
    		scanf("%d%d%d",&ch[i].x,&ch[i].y,&ch[i].z);
    		floor_limit=mymin(floor_limit,ch[i].x);
    		ceil_limit=mymax(ceil_limit,ch[i].y);
    	}
    	sort(ch+1,ch+n+1,cmp);
    	bt(floor_limit,ceil_limit);
    	int  ans=0;
    	for(int  i=1;i<=n;i++)
    	{
    		int  val=0;
    		if((val=findans(1,floor_limit,ceil_limit,ch[i].x,ch[i].y))<ch[i].z)
    		{
    			ans+=change(1,floor_limit,ceil_limit,ch[i].x,ch[i].y,ch[i].z-val);
    		}
    	}
    	printf("%d
    ",ans);
    	return  0;
    }
    

    但是要注意有一种贪心是错误的,即每一次选择最多集合覆盖的区域放数字,一个错误数据:

    8
    10 11 1
    9 12 1
    8 13 1
    7 14 1
    6 15 1
    2 10 1
    1 3 2
    2 7 2
    

    尤其是对于有相同的集合覆盖数的区域更加难以选择,因为选择的错误更加容易导致错误(反正这种贪心原本就是错的),比如:

    4
    1 2 1
    2 3 1
    3 4 1
    4 5 1
    

    第一个错误数据我想了好久才构造出来啊QAQ。

  • 相关阅读:
    [TimLinux] TCP全连接队列满
    [TimLinux] JavaScript 中循环执行和定时执行
    [TimLinux] JavaScript 事件
    [TimLinux] JavaScript 获取设置在CSS类中的属性值
    [TimLinux] JavaScript 面向对象程序设计
    [TimLinux] JavaScript 引用类型——Date
    [TimLinux] django html如何实现固定表头
    [TimLinux] Django 信号
    [TimLinux] Django 中间件
    安卓存储之文件存储方式j
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13689694.html
Copyright © 2011-2022 走看看