NOI2016区间bzoj4653(线段树,尺取法,区间离散化)
题目描述
在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) 。现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个位置。换句话说,就是使得存在一个 \(x\) ,使得对于每一个被选中的区间 \([l_i,r_i]\) ,都有 \(l_i≤x≤r_i\) 。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 \([l_i,r_i]\) 的长度定义为 \(r_i-l_i\) ,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 \(-1\) 。
输入输出格式
输入格式:
第一行包含两个正整数 \(N,M\) 用空格隔开,意义如上文所述。保证 \(1≤M≤N\)
接下来 \(N\) 行,每行表示一个区间,包含用空格隔开的两个整数 \(l_i\) 和 \(r_i\) 为该区间的左右端点。
\(N<=500000,M<=200000,0≤li≤ri≤10^9\)
输出格式:
只有一行,包含一个正整数,即最小花费。
首先,这个区间的数值非常的大,我们需要离散化
可是我QwQ不会呀
区间离散化是将左端点,和右端点放到同一个数组里,排序,然后依次赋值。
记得区间长度计算要在离散化之前
void init()
{
for (int i=1;i<=n;i++)
{
b[++cnt].val=a[i].l;
b[cnt].bel=1;b[cnt].num=i;
b[++cnt].val=a[i].r;
b[cnt].bel=2;b[cnt].num=i;
}
sort(b+1,b+1+cnt,cmp);
for (int i=1;i<=cnt;i++)
{
if (b[i].val!=b[i-1].val) ymh++;
if (b[i].bel==1) l[b[i].num]=ymh;
else r[b[i].num]=ymh;
}
for (int i=1;i<=n;i++)
{
a[i].l=l[i];
a[i].r=r[i];
}
//for (int i=1;i<=n;i++) printf("%d %d\n",a[i].l,a[i].r);
sort(a+1,a+1+n,cmp1);
}
离散化初始化完之后呢~
我们考虑这个题是要求使得最大区间减去最小区间的值最小,那么我们不妨按照区间长度排序
那么,如果判断一个点是否被覆盖了m次呢。
我们可以直接对于每个\([l,r]\)将里面的数都加一,然后统计区间最大值就可以了
那么就需要一个线段树了!
那.....之后呢QwQ,好像没什么用。
这时候就需要一个神奇的东西“尺取法”
尺取法:顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。之所以需要掌握这个技巧,是因为尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的
时候,所以尺取法是一种高效的枚举区间的方法,一般用于求取有一定限制的区间个数或最短的区间等等。当然任何技巧都存在其不足的地方,有些情况下尺取法不可行,无法得出正确答案
尺取法通常适用于选取区间有一定规律,或者说所选取的区间有一定的变化趋势的情况,通俗地说,在对所选取区间进行判断之后,我们可以明确如何进一步有方向地推进区间端点以求解满足条件的区间,如果已经判断了目前所选取的区间,但却无法确定所要求解的区间如何进一步
得到根据其端点得到,那么尺取法便是不可行的。首先,明确题目所需要求解的量之后,区间左右端点一般从最整个数组的起点开始,之后判断区间是否符合条件在根据实际情况变化区间的端点求解答案。
举个例子:
我们要求在给定的序列中求和等于x的区间的个数
那么我们对于当前区间\([l,r]\),如果当前的和小于x,就\(r++\) ,否则\(l++\)
回到这道题
我们先按照长度从小到大依次加入区间,如果当然已经存在一个出现次数为\(m\)
那么就更新答案,并跳\(l\),如果已知出现次数是m,那么就一直更新答案了
否则就一直跳r
QwQ不过跳的时候边界和更新与跳指针的先后要特别注意,详细直接看代码吧
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 500010;
struct Node{
int l,r,len;
};
struct pp{
int val,num,bel;
};
Node a[maxn];
pp b[2*maxn];
int n,m;
int f[10*maxn];
int cnt;
int st,ed;
int add[10*maxn];
int l[maxn],r[maxn];
bool cmp(pp a,pp b)
{
return a.val<b.val;
}
bool cmp1(Node a,Node b)
{
return a.len<b.len;
}
int ymh=0;
void init()
{
for (int i=1;i<=n;i++)
{
b[++cnt].val=a[i].l;
b[cnt].bel=1;b[cnt].num=i;
b[++cnt].val=a[i].r;
b[cnt].bel=2;b[cnt].num=i;
}
sort(b+1,b+1+cnt,cmp);
for (int i=1;i<=cnt;i++)
{
if (b[i].val!=b[i-1].val) ymh++;
if (b[i].bel==1) l[b[i].num]=ymh;
else r[b[i].num]=ymh;
}
for (int i=1;i<=n;i++)
{
a[i].l=l[i];
a[i].r=r[i];
}
//for (int i=1;i<=n;i++) printf("%d %d\n",a[i].l,a[i].r);
sort(a+1,a+1+n,cmp1);
}
void up(int root)
{
f[root]=max(f[root<<1],f[root<<1|1]);
}
void pushdown(int root,int l,int r)
{
if (add[root])
{
add[root<<1]+=add[root];
add[root<<1|1]+=add[root];
f[root<<1]=f[root<<1]+add[root];
f[root<<1|1]=f[root<<1|1]+add[root];
add[root]=0;
}
}
void update(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
f[root]=f[root]+p;
add[root]+=p;
return;
}
pushdown(root,l,r);
int mid =(l+r) >> 1;
if (x<=mid) update(root<<1,l,mid,x,y,p);
if (y>mid) update(root<<1|1,mid+1,r,x,y,p);
up(root);
}
int query(int root,int l,int r,int x,int y)
{
if (x<=l && r<=y)
{
return f[root];
}
pushdown(root,l,r);
int mid = (l+r) >> 1;
int ans=0;
if (x<=mid) ans=max(ans,query(root<<1,l,mid,x,y));
if (y>mid) ans=max(ans,query(root<<1|1,mid+1,r,x,y));
return ans;
}
int ans=2e9;
int main()
{
n=read(),m=read();
for (int i=1;i<=n;i++)
{
a[i].l=read(),a[i].r=read();
a[i].len=a[i].r-a[i].l;
}
init();
st=1;ed=1;
update(1,1,ymh,a[1].l,a[1].r,1);
while (ed<n && st<n)
{
while (query(1,1,ymh,1,ymh)<m && ed<n && st<n)
{
++ed;
update(1,1,ymh,a[ed].l,a[ed].r,1);
}
while(query(1,1,ymh,1,ymh)>=m && ed<n && st<n && st<ed)
{
if (query(1,1,ymh,1,ymh)==m)
ans=min(ans,a[ed].len-a[st].len);
update(1,1,ymh,a[st].l,a[st].r,-1);
++st;
}
}
if (ans==2e9) ans=-1;
cout<<ans<<endl;
return 0;
}