题目链接:https://codeforces.com/problemset/problem/1175/E
题目大意:给n个区间,询问m次,每次给出能覆盖【x,y】最少的区间数。
假如询问一次,可以考虑用贪心做,但是此题询问m次,可以用倍增的方法计算出来。
思路:
r【i】:表示从i开始能覆盖的最大的右端点值。
更新r[i]:(1)因为可能有多个区间从i开始,取右端点最大的1个。 时预处理, r[x]=max(r[x],y);
(2) i的左边i-1也可以更新r[i].r[i]=max(r[i-1],r[i]).
dp[j][i]:表示从i开始, 2^j个区间能到达的最大的右端点。
状态转移:dp[j][i]=dp[j-1][dp[j-1][i]]. (类似基本的倍增,将2^j个区间分为两个2^(j-1)区间求)。
#include <iostream>
#define MA 500005
using namespace std;
int dp[20][MA],n,m,x,y,r[MA];
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++){
cin>>x>>y;
r[x]=max(r[x],y);
}
dp[0][0]=r[0];
for(int i=1; i<MA; i++){
r[i]=max(r[i-1],r[i]);
dp[0][i]=r[i];
}
for(int j=1; j<20; j++){
for(int i=0; i<MA; i++){
dp[j][i]=dp[j-1][dp[j-1][i]];//关键
}
}
while(m--){
cin>>x>>y;
int cnt=0;
for(int i=19; i>=0;i--){
if(dp[i][x]<y){//每次寻找一个最大幂次值,使得dp[i][x]严格小于y,超过y可能会浪费多余的区间
x=dp[i][x];
cnt+=(1<<i);
}
}
if(dp[0][x]<y){//假如从x再走一步到不了y
cout<<"-1"<<endl;
}
else{//再从x取1个区间就可以覆盖了。
cout<<cnt+1<<endl;
}
}
return 0;
}