CodeForces 500E New Year Domino

题意:

从左到右排列着\(n\)个多米诺骨牌,它们分别站在\(x\)轴上的位置\(p_i\)上且高度为\(l_i\)
当第\(i\)个多米诺骨牌向右倒下时,如果\(p_i < p_j \leq p_i + l_i\)那么第\(j\)个多米诺骨牌也会倒下,以此类推。
然后有\(q\)个询问\([x, \, y]\),要推倒第\(x\)个多米诺骨牌,而且最终要使得第\(y\)个多米诺骨牌倒下。
为了使第\(y\)个倒下,可以加长某些牌的长度。
对于每个询问,求最少加长的总长度之和。

分析:

对于第\(i\)个牌,定义\(R_i\)为推倒第\(i\)个牌,所倒下的牌中\(p_j+l_j\)的最大值。
有递推式:\(R_i=max \{ p_i+l_i, \, max\{ R_j | p_i < p_j \leq p_i+l_i \} \}\)
\(R_i\)可以通过维护线段树计算得到。
接下来根据\(R_i\)计算\(U_i\),表示推倒第\(i\)个牌后,最左边没有倒下的牌的编号。
因此从\(x\)\(U_x\),我们至少需要增加\(p_{U_{x}} - R_x\)的长度。
所以我们向右一步一步地加,直到第\(y\)块倒下为止。
但是这样每次查询的复杂度为\(O(n)\)的。
所以还需要二进制优化一下,类似于求\(LCA\)的倍增算法。
\(anc(i, \, j)\)表示迭代\(2^j\)\(U_i\)最后得到的牌的编号,\(cost(i, \, j)\)表示相应增加的牌的长度。
\(O(nlogn)\)预处理一下,就可以做到\(O(logn)\)查询。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 200000 + 10;

int n, q;
int p[maxn], l[maxn];
int R[maxn], U[maxn];

int maxv[maxn << 2];

void update(int o, int L, int R, int p, int v) {
	if(L == R) { maxv[o] = v; return; }
	int M = (L + R) / 2;
	if(p <= M) update(o<<1, L, M, p, v);
	else update(o<<1|1, M+1, R, p, v);
	maxv[o] = max(maxv[o<<1], maxv[o<<1|1]);
}

int query(int o, int L, int R, int qL, int qR) {
	if(qL <= L && R <= qR) { return maxv[o]; }
	int M = (L + R) / 2;
	int ans = 0;
	if(qL <= M) ans = max(ans, query(o<<1, L, M, qL, qR));
	if(qR >  M) ans = max(ans, query(o<<1|1, M+1, R, qL, qR));
	return ans;
}

int anc[maxn][20], cost[maxn][20];

int lb(int l, int r, int x) {
	while(l < r) {
		int mid = (l + r) / 2 + 1;
		if(p[mid] <= x) l = mid;
		else r = mid - 1;
	}
	return l;
}

int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d%d", p + i, l + i);
	for(int i = n; i; i--) {
		R[i] = p[i] + l[i];
		int lft = i + 1;
		int rgh = lb(1, n, p[i] + l[i]);
		if(lft <= rgh) R[i] = max(R[i], query(1, 1, n, lft, rgh));
		update(1, 1, n, i, R[i]);
	}
	for(int i = 1; i <= n; i++) {
		U[i] = upper_bound(p + 1, p + 1 + n, R[i]) - p;
		if(U[i] == n + 1) U[i]--;
	}

	for(int i = 1; i <= n; i++) {
		anc[i][0] = U[i];
		cost[i][0] = max(0, p[U[i]] - R[i]);
	}
	for(int j = 1; (1 << j) < n; j++)
		for(int i = 1; i <= n; i++) if(anc[i][j-1] != n) {
			int t = anc[i][j-1];
			anc[i][j] = anc[t][j-1];
			cost[i][j] = cost[i][j-1] + cost[t][j-1];
		}

	scanf("%d", &q);
	while(q--) {
		int x, y; scanf("%d%d", &x, &y);
		int ans = 0;
		for(int i = 19; i >= 0; i--) if(anc[x][i] && anc[x][i] <= y) {
			ans += cost[x][i];
			x = anc[x][i];
		}
		if(x < y) ans += max(0, p[y] - p[U[x]]);
		printf("%d\n", ans);
	}

	return 0;
}
posted @ 2015-12-29 06:54  AOQNRMGYXLMV  阅读(370)  评论(0编辑  收藏  举报