题意:给你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);
}
}