[UOJ #222][NOI2016]区间(线段树)

Description

在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值

求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。

Solution

对区间的长度排序,离散化,然后线段树维护每个点被覆盖过的次数,根据决策的单调性就可做了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define MAXN 500005
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int a[MAXN*2],cnt=0;
int read()
{
    int x=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){
        if(c==‘-‘)f=-1;c=getchar();
    }
    while(c>=‘0‘&&c<=‘9‘){
        x=x*10+c-‘0‘;c=getchar();
    }
    return x*f;
}
struct Node1
{
    int L,R,len;
}d[MAXN];
bool cmp(Node1 x,Node1 y)
{
    return x.len<y.len;
}
struct Node2
{
    int l,r,tag,maxn;
}t[MAXN*8];
void pushdown(int idx)
{
    if(t[idx].tag&&t[idx].l!=t[idx].r)
    {
        int tag=t[idx].tag;
        t[idx].tag=0;
        t[idx<<1].tag+=tag;
        t[idx<<1|1].tag+=tag;
        t[idx<<1].maxn+=tag;
        t[idx<<1|1].maxn+=tag;
    }
}
void update(int idx)
{
    t[idx].maxn=max(t[idx<<1].maxn,t[idx<<1|1].maxn);
}
void build(int idx,int l,int r)
{
    t[idx].l=l,t[idx].r=r,t[idx].maxn=t[idx].tag=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(idx<<1,l,mid);
    build(idx<<1|1,mid+1,r);
}
void add(int idx,int x,int y,int z)
{

    if(x<=t[idx].l&&y>=t[idx].r)
    {
        t[idx].maxn+=z;
        t[idx].tag+=z;
        return;
    }
    pushdown(idx);
    int mid=(t[idx].l+t[idx].r)>>1;
    if(y<=mid)add(idx<<1,x,y,z);
    else if(x>mid)add(idx<<1|1,x,y,z);
    else add(idx<<1,x,y,z),add(idx<<1|1,x,y,z);
    update(idx);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        d[i].L=read(),d[i].R=read();
        a[++cnt]=d[i].L,a[++cnt]=d[i].R;
        d[i].len=d[i].R-d[i].L;
    }
    sort(a+1,a+1+cnt);
    cnt=unique(a+1,a+1+cnt)-a-1;
    sort(d+1,d+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        d[i].L=lower_bound(a+1,a+1+cnt,d[i].L)-a;
        d[i].R=lower_bound(a+1,a+1+cnt,d[i].R)-a;
    }
    int ans=INF,top=0;
    build(1,1,cnt);
    for(int i=1;i<=n;i++)
    {
        while(top<n&&t[1].maxn<m)
        {
            ++top;
            add(1,d[top].L,d[top].R,1);
        }
        if(t[1].maxn>=m)ans=min(ans,d[top].len-d[i].len);
        add(1,d[i].L,d[i].R,-1);
    }
    if(ans==INF)printf("-1\n");
    else printf("%d\n",ans);
    return 0;
} 
时间: 2024-12-06 04:48:50

[UOJ #222][NOI2016]区间(线段树)的相关文章

UOJ222 NOI2016 区间 线段树+FIFO队列

首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询问覆盖次数可以用线段树实现 1 //C++11 code 2 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 7 const int maxN=500005; 8 const int inf

Poj3225Help with Intervals区间线段树

这题不说了,都是泪.这题也是拆点. #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include <list> #include <set&g

【BZOJ3110】【Zjoi2013】K大数查询 树套树 权值线段树套区间线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020009"); } 题解: 外层权值线段树,内层区间线段树可解. 权值都是1~n,就不用离散化了. 我写了标记永久化. 其它心得神马的: 天生对树形数据结构无爱. 第一次写树套树,终于知道是怎么回事了. (只针对本题) 就是外层每个点都表示了一段

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

【BZOJ-4653】区间 线段树 + 排序 + 离散化

4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 107  Solved: 70[Submit][Status][Discuss] Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选

luogu 1712 区间(线段树+尺取法)

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间. 接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关. 这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣. 不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间

【树链剖分(区间线段树)】BZOJ4196-[NOI2015]软件包管理

[题目大意] 如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B.同时,如果想要卸载软件包B,则必须卸载软件包A.而且,由于你之前的工作,除0号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包.依赖关系不存在环.求出在安装和卸载某个软件包时,实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包.(注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安

P1890 gcd区间 线段树

题目描述 给定一行\(n\)个正整数\(a[1]..a[n]\). \(m\)次询问,每次询问给定一个区间\([L,R]\),输出\(a[L]..a[R]\)的最大公因数. 输入格式 第一行两个整数\(n,m\). 第二行n个整数表示\(a[1]..a[n]\). 以下\(m\)行,每行\(2\)个整数表示询问区间的左右端点. 保证输入数据合法. 输出格式 共m行,每行表示一个询问的答案. 输入输出样例 输入 #1 5 3 4 12 3 6 7 1 3 2 3 5 5 输出 #1 1 3 7 说

bzoj3226: [Sdoi2008]校门外的区间 线段树

题比较有趣,输入输出比较麻烦. 每个点拆成两个,线段树维护.(这题难点真的在输入输出) #include<bits/stdc++.h> #define N (1<<17) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=N,int k=1