刷题向》关于一道尺取法的神题目(BZOJ4653)(HARD-)(BZOJ 30题纪念)

  不得不说,这也许会是一道长期在我的博客里作为“HARD”难度存在的题

  这道题能很好的考验选手的思考能力,但本蒟蒻最后还是听了省队爷讲了之后才会。。。(默默面壁)

  题目里,说对于每一个点,是用当前选出的M个里面,最长长度减去最短长度作为价值。也就是说:选择长度介于最长与最短之间的边,是对答案没有影响的。(本蒟蒻并没有想到这一点。。。)

  所以由于这一点,我们可以先对于边的长度排序。

  那么题目中提到的M,是“选中多余或等于M条边”,从这里就可以看出,我们只需要选定一个头和一个尾就好,由此可以看出,这个子问题完全可以用尺取法处理。

  以尺取法不停更新最优解就好啦。

  那么怎么处理M捏,其实很简单,用离散化加线段树就可以解决关于“M个点“的子问题。

  直接甩题目&代码

  

Description

在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。

Input

第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9

Output

只有一行,包含一个正整数,即最小花费。

Sample Input

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

Sample Output

2
  1 /**************************************************************
  2     Problem: 4653
  3     User: PencilWang
  4     Language: C++
  5     Result: Accepted
  6     Time:12896 ms
  7     Memory:87320 kb
  8 ****************************************************************/
  9  
 10 #include<stdio.h>
 11 #include<string.h>
 12 #include<algorithm>
 13 #include<set>
 14 using namespace std;
 15 struct shit1{int L,R,lon;}e[1010000];
 16 struct shit2{int L,R,lazy,num;}s[3030000];
 17 int fucker[1010000];
 18 int ans,n,m,k;
 19 bool cmp(shit1 a,shit1 b)
 20 {
 21     return a.lon<b.lon;
 22 }
 23 void push_down(int p)
 24 {
 25     int L=p<<1,R=p<<1|1;
 26     s[L].num+=s[p].lazy;
 27     s[L].lazy+=s[p].lazy;
 28     s[R].lazy+=s[p].lazy;
 29     s[R].num+=s[p].lazy;
 30     s[p].lazy=0;
 31     return ;
 32 }
 33 void push_up(int p)
 34 {
 35     s[p].num=max(s[p<<1].num,s[p<<1|1].num);
 36     return ;
 37 }
 38 void build(int p,int L,int R)
 39 {
 40     s[p].L=L,s[p].R=R;
 41     if(L==R)return ;
 42     int mid=(L+R)>>1;
 43     build(p<<1,L,mid);
 44     build(p<<1|1,mid+1,R);
 45     return ;
 46 }
 47 void add(int a,int b,int p,int num)
 48 {
 49     if(a<=s[p].L&&s[p].R<=b)
 50     {
 51         s[p].num+=num;
 52         s[p].lazy+=num;
 53         return ;
 54     }
 55     push_down(p);
 56     int mid=(s[p].L+s[p].R)>>1;
 57     if(a<=mid)add(a,b,p<<1,num);
 58     if(b>mid)add(a,b,p<<1|1,num);
 59     push_up(p);
 60     return ;
 61 }
 62 set<int>sb;
 63 int main()
 64 {
 65     scanf("%d%d",&n,&k);
 66     for(int i=1;i<=n;i++)
 67     {
 68     scanf("%d%d",&e[i].L,&e[i].R);
 69     sb.insert(e[i].L);sb.insert(e[i].R);
 70     e[i].lon=e[i].R-e[i].L+1;
 71     }
 72     for(set<int>::iterator p=sb.begin();p!=sb.end();++p)
 73     fucker[++m]=*p;
 74     build(1,1,m);
 75     sort(e+1,e+n+1,cmp);
 76     for(int i=1;i<=n;i++)
 77     {
 78     e[i].L=lower_bound(fucker+1,fucker+m+1,e[i].L)-fucker;
 79     e[i].R=lower_bound(fucker+1,fucker+m+1,e[i].R)-fucker;
 80     }
 81     int L=1,R=1;
 82     int Lz=e[1].lon,Rz=e[1].lon;
 83     while(e[R].lon==Rz)
 84     add(e[R].L,e[R].R,1,1),R++;
 85     R--;
 86     ans=0x3f3f3f3f;
 87     while(L<=R&&R<=n)
 88     {
 89         if(s[1].num>=k)
 90         {
 91         ans=min(ans,Rz-Lz);
 92         while(e[L].lon==Lz)
 93             add(e[L].L,e[L].R,1,-1),L++;
 94             Lz=e[L].lon;
 95         }
 96         else
 97         {
 98         Rz=e[++R].lon;
 99         while(e[R].lon==Rz&&R<=n)
100             add(e[R].L,e[R].R,1,1),R++;
101             if(R==n+1)break;
102             R--;
103         }
104     }
105     if(ans==0x3f3f3f3f)ans=-1;
106     printf("%d",ans);
107     return 0;
108 }

 

posted @ 2016-10-27 11:19  PencilWang  阅读(284)  评论(0编辑  收藏  举报