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

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费。花费指的是选择的区间中最大长度减去最小长度。

坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间。

接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关。

这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣。

不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间尽量的往右移,直到满足有一个点覆盖次数>=m。

这不就是尺取法吗。。。所以我们需要维护一个数据结构支持区间加减法,区间查询最大值。显然线段树可以满足这个要求。

因此总的时间复杂度就是O(nlogn).

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-7
# define MOD 1024523
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=500005;
//Code begin...

int seg[N<<4], tag[N<<4];
struct Node{int l, r;}node[N];
VI v;

bool comp(Node a, Node b){return a.r-a.l<b.r-b.l;}
void push_up(int p){seg[p]=max(seg[p<<1],seg[p<<1|1]);}
void push_down(int p){
    if (!tag[p]) return ;
    seg[p]+=tag[p]; tag[p<<1]+=tag[p]; tag[p<<1|1]+=tag[p]; tag[p]=0;
}
void update(int p, int l, int r, int L, int R, int val){
    push_down(p);
    if (L>r||R<l) return ;
    if (L<=l&&R>=r) tag[p]+=val, push_down(p);
    else {
        int mid=(l+r)>>1;
        update(lch,L,R,val); update(rch,L,R,val); push_up(p);
    }
}
int query(int p, int l, int r, int L, int R){
    push_down(p);
    if (L>r||R<l) return 0;
    if (L<=l&&R>=r) return seg[p];
    int mid=(l+r)>>1;
    return max(query(lch,L,R),query(rch,L,R));
}
int main ()
{
    int n, m;
    n=Scan(); m=Scan();
    FOR(i,1,n) node[i].l=Scan(), node[i].r=Scan(), v.pb(node[i].l), v.pb(node[i].r+1);
    sort(v.begin(),v.end()); sort(node+1,node+n+1,comp);
    int siz=unique(v.begin(),v.end())-v.begin()+1;
    int l=1, r=0, ans=INF, L, R;
    while (l<=n&&r<=n) {
        while (r<=n) {
            int val=query(1,1,siz,1,siz);
            if (val>=m) {ans=min(ans,node[r].r-node[r].l-node[l].r+node[l].l); break;}
            ++r; L=lower_bound(v.begin(),v.begin()+siz,node[r].l)-v.begin()+1;
            R=lower_bound(v.begin(),v.begin()+siz,node[r].r+1)-v.begin();
            update(1,1,siz,L,R,1);
        }
        L=lower_bound(v.begin(),v.begin()+siz,node[l].l)-v.begin()+1;
        R=lower_bound(v.begin(),v.begin()+siz,node[l].r+1)-v.begin();
        update(1,1,siz,L,R,-1); ++l;
    }
    if (ans==INF) puts("-1");
    else printf("%d\n",ans);
    return 0;
}

时间: 2024-10-11 13:19:51

luogu 1712 区间(线段树+尺取法)的相关文章

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

[Luogu] 可持久化线段树 1(主席树)

https://www.luogu.org/problemnew/show/P3834 #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn = 2e5 + 10; #define RR freopen("gg.in", "r", stdin) int n

hihocoder-1483区间价值 (二分+尺取法)

题目链接: 区间价值 给定n个数A1...An,小Ho想了解AL..AR中有多少对元素值相同.小Ho把这个数目定义为区间[L,R]的价值,用v[L,R]表示. 例如1 1 1 2 2这五个数所组成的区间的价值为4. 现在小Ho想知道在所有的的v[L,R](1 <= L <= R <= n)中,第k小的值是多少. Input 第一行一个数T(T<=10),表示数据组数. 对于每一组数据: 第一行两个数n,k(1<=n<=200,000,1<=k<=n*(n+1

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

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

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

【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. 对于一个合法的选

[bzoj 1798][luogu p2023]Seq 线段树Seq

题目大意: 维护一个数列,支持区间乘,区间加,求区间和. 线段树题,对于乘和加操作我们可以维护一个标记.对于乘用乘法分配律分解. 代码如下: 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int N = 100010; 7 int inline getint() 8 { 9