一道很有意思的贪心+倍增的题目。
首先我们要把环变成链(复制一倍放到末尾去)。
这道题有一个非常巧妙的性质:任意一个区间都没有别的被包含(假如这一道题有包含的话就要把被包含的那个区间给去掉)。也就是说把把每一个士兵的区间左端点从小到大排序后,右端点也是从小到大排好序的。然后我们从贪心的角度思考,对于一个士兵,接应他的人一定是在能接应到的情况下左端点最大的。
然后就很好做了,考虑一下倍增,
n
x
t
[
x
]
[
i
]
nxt[x][i]
nxt[x][i]表示i士兵以后的第
2
i
2^i
2i个人。对于每一个士兵,只需要用log级的时间像lca一样跳即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 2e5 + 6;
int n, m, res[N];
struct node {
int l, r, id;
} s[N<<1];
bool cmp(node p1, node p2) {
return p1.l < p2.l;
}
int nxt[N<<1][21];
void prepare() {
for (int i = 1, p = i; i <= 2 * n; i++) {
while (p <= 2 * n && s[p].l <= s[i].r) p++;
nxt[i][0] = p - 1;
}
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= 2 * n; j++)
nxt[j][i] = nxt[nxt[j][i - 1]][i - 1];
}
void solve(int k) {
int end = s[k].l + m, ans = 1, p = k;
for (int i = 20; i >= 0; i--)
if (nxt[k][i] != 0 && s[nxt[k][i]].r < end)
ans += (1 << i), k = nxt[k][i];
res[s[p].id] = ans + 1;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i].l >> s[i].r;
if (s[i].r < s[i].l) s[i].r += m;
s[i].id = i;
}
sort(s + 1, s + n + 1, cmp);
for (int i = 1; i <= n; i++) {
s[i + n] = s[i];
s[i + n].l = s[i].l + m;
s[i + n].r = s[i].r + m;
}
prepare();
for (int i = 1; i <= n; i++) solve(i);
for (int i = 1; i <= n; i++) cout << res[i] << " ";
return 0;
}