BZOJ 4444
倍增 + 贪心。
发现是一个环,先按照套路把环断开复制一倍,这样子的话覆盖完整个环就相当于覆盖一条长度不小于$m$的链,注意这样子有一些区间在新的这条链上会出现两次。
我们为了找到最小的满足要求的答案,在选择完一个区间$[l, r]$之后会选择左端点不超过$r$但是右端点尽量大的区间,因为题目保证了所有的区间不相互包含,这样子的话我们只要找到左端点最靠右的区间就可以了。
用$f_{i, j}$表示$i$号区间向下跳$2^j$个区间能跳到的最后一个区间,接下来只要按照套路倍增一下就好了。
注意弄一个最大的右端点(要足够大,我就是这样WA了两次……),使倍增的时候能停下来。
时间复杂度$O(nlogn)$。
实现的时候需要注意细节。
#include <cstdio> #include <cstring> #include <climits> #include <algorithm> using namespace std; typedef long long ll; const int N = 4e5 + 5; const int Lg = 22; const int inf = INT_MAX; int n, m, tot = 0, ans[N], f[N][Lg]; struct Seg { int l, r, id; friend bool operator < (const Seg &x, const Seg &y) { if(x.l != y.l) return x.l < y.l; else return x.r < y.r; } } a[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int solve(int x) { int res = 2, pos = x; for(int i = 20; i >= 0; i--) if(f[pos][i] && a[f[pos][i]].r < a[x].l + m) pos = f[pos][i], res += (1 << i); return res; } int main() { // freopen("3.in", "r", stdin); // freopen("my.out", "w", stdout); read(n), read(m); for(int l, r, i = 1; i <= n; i++) { read(l), read(r); if(l > r) { r += m; a[++tot].l = l, a[tot].r = r, a[tot].id = i; } else { a[++tot].l = l, a[tot].r = r, a[tot].id = i; a[++tot].l = l + m, a[tot].r = r + m, a[tot].id = i; } } sort(a + 1, a + 1 + tot); a[tot + 1].r = inf; for(int j = 1, i = 1; i <= tot; i++) { for(; j <= tot && a[j + 1].l <= a[i].r; ++j); f[i][0] = j; } for(int j = 1; j <= 20; j++) for(int i = 1; i <= tot; i++) f[i][j] = f[f[i][j - 1]][j - 1]; for(int i = 1; i <= tot; i++) { if(a[i].l > m) continue; ans[a[i].id] = solve(i); } for(int i = 1; i <= n; i++) printf("%d%c", ans[i], i == n ? ' ' : ' '); // printf(" "); return 0; }