CF1175E Minimal Segment Cover 题解

题意:给出\(n\)个形如\([l,r]\)的线段。\(m\)次询问,每次询问区间\([x,y]\),问至少选出几条线段,使得区间\([x,y]\)的任何一个部位都被至少一条线段覆盖。

首先有一个显然的贪心,设询问区间为\([l,r]\),则所取的区间最靠左的一个的左端点必定小于等于\(l\),并且要使这个区间尽可能地向右延伸。
取完第一个之后,重复以上的贪心,直至满足条件,那么这样的答案必定是最小的。

但是这样的时间复杂度是\(O(nm)\)的,所以要考虑优化,优化方法是用倍增,每次向右跳\(2^i\)条线段,那么复杂度就变成\(O(n\log m)\)了。于是本题就解决了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define M 500005
inline int read()
{
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    int ans=0;
    while(isdigit(ch))
    {
        ans=ans*10+ch-48;
        ch=getchar();
    }
    return ans;
}
int suf[M][25];

int main()
{
    int n,m;
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=n;i++)
    {
        x=read(),y=read();
        x++;
        if(suf[x][0]<y)
        {
            suf[x][0]=y;
        }
    }
    for(int i=1;i<=M;i++)
    {
        suf[i][0]=max(suf[i][0],suf[i-1][0]);
    }
    for(int i=1;i<=19;i++)
    {
        for(int j=1;j<=M;j++)
        {
            suf[j][i]=suf[suf[j][i-1]+1][i-1];
        }
    }
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read();
        int ans=0;
        for(int j=19;j>=0;j--)
        {
            if(suf[x+1][j]<y&&suf[x+1][j]>x)
            {
                x=suf[x+1][j];
                ans+=(1<<j);
            }
        }
        ans++;x=suf[x+1][0];
        if(x<y) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wasa855/p/10989032.html

时间: 2024-10-05 05:50:05

CF1175E Minimal Segment Cover 题解的相关文章

CF1175E Minimal Segment Cover

题目链接 题意 给出n条线段.m次询问,每次询问给出一个区间\([l,r]\)问最少需要多少条线段才能覆盖区间\([l,r]\). 所有坐标\(\le 5\times 10^5\).\(n,m\le 2\times 10^ 5\) 思路 其实是比较经典的线段覆盖问题. \(f[i][j]\)表示从i开始走\(2^j\)条线段最远到达的位置. 然后对于每次询问都走一遍即可. 代码 /* * @Author: wxyww * @Date: 2019-06-06 10:55:48 * @Last Mo

CodeForces-1175E Minimal Segment Cover

题目描述 现有\(n\)条线段,每条线段的左右端点为\(L_i,R_i\),保证\(L_i \le R_i\). 有\(m\)个询问,每次查询\(X_i,Y_i\)区间内所有点被覆盖所需的线段的最小值. Input 输入第一行包含两个整数\(n,m\),含义如上所述. 接下来,有\(n\)行,每行有两个整数. 第\(i+1\)行包含\(2\)个整数,分别表示\(L_i,R_i\) . 接下来\(m\)行,表示\(m\)个询问. \(1 \le n,m \le 2e5\) \(0 \le L_i

codeforce 1175E Minimal Segment Cover ST表 倍增思想

这题太巧妙了. 题意是,给定2*10^5个区间.然后2*10^5组询问,每次询问一个区间,问至少需要几个给定区间,才能将其完全覆盖.坐标范围5*10^5. 如果只有一个询问区间,是经典的贪心问题.我们每次选择,尽可能覆盖的靠右的区间. 但是这题显然贪心的话,时间是不够的. 考虑使用倍增进行预处理. 我们用dp[i][o]表示从i点开始,选择o个区间,能覆盖到最远哪个点.为-1,则表示不存在. 那么显然如果dp[i][o - 1] != -1 时,dp[i][o] = dp[dp[i][o - 1

HDU 1166 敌兵布阵 Segment Tree题解

本题是最基本的分段树操作了.或者一般叫线段树,不过好像和线段没什么关系,只是分段了. 不使用lazy标志,更新只是更新单点. 如果不使用分段树,那么更新时间效率只需要O(1),使用分段树更新效率就需要O(lgn)了. 但是不是用分段树,那么查询的时间效率是O(n),而分段树查询效率是O(lgn) 这就是amortize分摊了时间,而且lgn真的很快,数据不是非常巨大的时候,接近常数了. 故此本题需要使用分段树. #include <cstdio> class EnemyInfo { stati

HDU 1754 I hate it 分段树Segment Tree题解

给出一段数据,然后要更新单个数据,会询问一段数据中的最大值. 又是一道分段树操作.渐渐熟手了. #pragma once #include <cstdio> #include <algorithm> using namespace std; class IHateIt_1754_1 { static const int SIZE = 200001; int *segTree; //不要使用segTree[SIZE<<2] inline int lChild(int r)

Gym 100431E Word Cover 题解:KMP神用

题意: 给你一个串,问你他的每个前缀的最小重复单元,其中单元是可以重叠的,最后按顺序输出即可.比如样例中abaabaa的最小重复单元为abaa,所以相应输出为4. 样例: input : abaabaababa outpit:1 2 3 4 5 3 4 5 3 10 3 kmp过程就不用多说了,现在我们利用next数组的性质来对问题进行求解. 我们首先用一个ans[maxn]数组来记录最后的答案,且我们的字符串下标从0开始,显然,我们ans[i]的最大值为i+1,我们因此也用此值对其进行初始化.

Educational Codeforces Round 66 (Rated for Div. 2)

a题教育我:不手写2个测试用例,就不要写代码,谢谢. b题真的恶心,判断溢出自己之前从没思考过的问题.最后用了很尴尬的判断,a=b+c的时候,三个数不能>=(1<<32),不能小于0,不知道为什么,也不想知道. c题是思维gap的味道,也靠推导能力,看你有没有思维jump的能力.用到了我特别喜欢的一种思考模式:设想答案长什么样子.答案肯定是中点离着两边一样远,才能最小化d(k),然后这个区间必然是覆盖了k个点,所以排序后固定长度的滑窗走起- d题.原来每次打比赛之前,我都在本子上手写两条

Codeforces Edu Round 66 A-E

A. From Hero to Zero 通过取余快速运行第一步即可.由于\(a \% b (a >= b) <= \frac{a}{2}\).所以总复杂度不超过\(O(log_2n)\). #include <cstdio> #include <iostream> using namespace std; typedef long long LL; int main(){ int T; scanf("%d", &T); while(T--)

并查集知识学习

(转) 并查集的作用:并和查,即合并和查找,将一些集合合并,快速查找或判断某两个集合的关系,或某元素与集合的关系,或某两个元素的关系. 并查集的结构:并查集主要操作对象是森林,树的结构赋予它独特的能力,对整个集合操作转换为对根节点(或称该集合的代表元素)的操作,一个集合里的元素关系不一定确定,但相对于根节点的关系很明了,这也是为了查找方便. 并查集优化方法:按秩合并和路径压缩的配合使用,使得查找过程优化到极致.按秩合并,每次将深度小的树合并到深度大的树里面去,使得整棵树尽量矮:路径压缩,将当前节