loj2007「SCOI2015」国旗计划

一道很有意思的贪心+倍增的题目。
首先我们要把环变成链(复制一倍放到末尾去)。
这道题有一个非常巧妙的性质:任意一个区间都没有别的被包含(假如这一道题有包含的话就要把被包含的那个区间给去掉)。也就是说把把每一个士兵的区间左端点从小到大排序后,右端点也是从小到大排好序的。然后我们从贪心的角度思考,对于一个士兵,接应他的人一定是在能接应到的情况下左端点最大的。
然后就很好做了,考虑一下倍增, n x t [ x ] [ i ] nxt[x][i] nxt[x][i]表示i士兵以后的第 2 i 2^i 2i个人。对于每一个士兵,只需要用log级的时间像lca一样跳即可。

#include 
#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(loj,各省省选)