E. Minimal Segment Cover(dp)

6 篇文章 0 订阅

传送门

题意:给你n个线段,m次询问,让你输出n条线段中能完全覆盖询问的线段的最小数量

题解:乍一看有点像区间覆盖问题,但是它n和m给你的是5e5,很大,所以对于询问应该近可能快速的得出答案,同时对于序列的操作也只能在询问前进行;通过对线段的处理,我们可以得到每个点只用一条线段最远能跳到哪个位置,同理用俩条线段最远能跳到哪也可以求出,然后如果跳x条线段之后就大于了右端点,最小数量就是x,如果有不能到的点就可以直接输出-1跳出。

    上面思路可行,但是复杂度和空间都是n平方级别,是不能接受的,但是可以对其进行一定程度的优化,第一维不变,第二维变成跳2的i次方跳线段最远可以到的位置,那么在询问时就变成了如果跳2的i次方会超过询问右端点,那么在当前节点就只向右跳i-1次方,然后从新端点继续往右跳,自到跳一条线段也会大于等于右端点时跳出,输出答案,对于-1的情况判断同上

AC代码:

#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#define ll long long
using namespace std;
const int maxn=5e5+5;
//const ll inf=9223372036854775800;
//const int inf=1e9;
//const int mod=1e9+7;
int rmq[maxn][22],n,m,dp[maxn],er[21];//rmq级录在i节点往后跳2的j次方条线段最多能到哪个点
struct node{
    int li,ri;
};
node y[maxn];
bool cmp(node a,node b){
    return a.li<b.li;
}
void getrmq( ){
    memset(dp,-1,sizeof(dp));
    memset(rmq,-1,sizeof(rmq));
    int i=y[1].ri;
    for(int a=2,j=0;a<=n;a++){
        if(y[a].li==y[a-1].li)
            i=max(i,y[a].ri);
        else{
            for(j=y[a-1].li;j<y[a].li;j++)
                dp[j]=i;
            i=max(i,y[a].ri);
        }
    }
    for(int j=y[n].li;j<i;j++)
        dp[j]=i;
    n=i;
    for(;i>=0;i--){
        rmq[i][0]=dp[i];
        if(dp[i]==-1)
            continue;
        int j;
        for(j=1;rmq[i][j-1]<n&&j<=20;j++){
            int k=rmq[i][j-1];
            if(rmq[k][j-1]==rmq[i][j-1])
                break;
            rmq[i][j]=rmq[k][j-1];
        }
            for(;j<=20;j++)
            rmq[i][j]=rmq[i][j-1];
    }
}
int main( ){
    er[0]=1;
    for(int a=1;a<20;a++)
        er[a]=er[a-1]*2;
    scanf("%d%d",&n,&m);
    for(int a=1;a<=n;a++)
        scanf("%d%d",&y[a].li,&y[a].ri);
    sort(y+1,y+n+1,cmp);
    getrmq();
    while(m--){
        int l,r;
        scanf("%d%d",&l,&r);
        //printf("%d\n",dp[l]);
        ll ans=0;
        bool judge;
        while(l<r){
            int a;
        for(a=0,judge=true;a<19;a++){
            //printf("%d %d %d\n",rmq[l][a],l,rmq[l][a+1]);
            if(rmq[l][a+1]>=r){
            ans+=er[a];
            l=rmq[l][a];
            judge=false;
            break;
            }
            else if(rmq[l][a+1]==-1){
                ans=-1;
                break;
            }
        }
        //printf("**%d %d %lld\n",l,r,ans);
        if(ans==-1||judge){
            ans=-1;
            break;
        }
        }
        printf("%lld\n",ans);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值