给定一个数轴,给定n条线段,每条线段可以覆盖[li,ri]的位置,q次询问,每次问如果要覆盖[l,r],最少要选多少条线段。
先考虑一个贪心的暴力解法:
对于询问[L,R],假设当前位置为l,我们要从左端点小于等于l的线段中找到一个最大的右端点r,并让当前位置更新为r,直到R==r
。
尽管我们可以预处理得到a[i]
表示从i位置只选一个线段能到达右边最远的位置是哪里, 暴力解法时间复杂度仍为 O(nq).
既然i位置能够选一条线段最远到达a[i],同时a[i]位置能选一条线段最远到达a[a[i]],那么i位置可以选x条线段最远到达的地方也是可以求的,(相当于我们知道每个位置走一步到达的地方,那么就可以求出每个位置走k步到达的地方),但是遍历x次去求太慢了。
我们把x写成二进制,假设为1011,那么我们只要知道i走1000,10,1步到达的地方,就可以求出x步的答案了,这就是倍增的思想,通过二进制表示使得遍历[1,x]的时间复杂度降到logx .
关于倍增的讲解可以参考:大佬的文章
回到这个问题,我们通过倍增在nlogn的预处理时间内,求出了每个点选x条线段能够到达的最远地方,那么每次询问,我们就从l位置出发,求出这个最小的x。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e2 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7