CF 66 EDIV2 E. Minimal Segment Cover //倍增
题意:给n条线段,给q个询问,每次询问给一个区间,求覆盖区间至少需要几条线段。
思路:倍增,定义dp[i][j],j条线段,从<=i的位置出发,能到的最右的下标。
其中dp[i][0]=max(dp[i-1][0],从i出发的线段能到的最大位置)。
询问的时候,先找到<r最远可以到的位置,然后再像右走一步就是>=r的位置。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MXN = 5e5;
const int MXLOG = 18;
int dp[MXN+2][MXLOG+2];
int two[20];
int query(int x,int y){ //二进制,直接贪心取
int re=0;
for(int k=MXLOG;k>=0;k--){
if(dp[x][k]<y) x=dp[x][k],re+=two[k];
}
if(dp[x][0]>=y) return re+1;
return -1;
}
int main(){
two[0]=1;for(int i=1;i<=19;i++) two[i]=two[i-1]*2;
int n,m;cin>>n>>m;
for(int i=0;i<=MXN;i++) dp[i][0]=i;
while(n--){
int l,r;scanf("%d%d",&l,&r);
dp[l][0]=max(dp[l][0],r);
}
for(int i=1;i<=MXN;i++) dp[i][0]=max(dp[i][0],dp[i-1][0]);
for(int i=1;i<=MXLOG;i++){
for(int j=0;j<=MXN;j++){
dp[j][i]=dp[dp[j][i-1]][i-1];
}
}
while(m--){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",query(l,r));
}
return 0;
}