[HNOI/AHOI2018]转盘

题目链接

题意:

给你一个\(n\)元环,你可以在0时刻从任意一个位置出发,每一秒可以选择往后或者留在原地
每个点有个参数\(T_i\),当你走到\(i\)的时间\(t>=T_i\)时你就可以把i标记
问你把整个环上的点都标记最小需要多长时间,带修改\(T_i\),强制在线


好难的题。
首先,有等待操作不太好弄。
可以发现:在总时间一定的情况下,到每个点越晚越好。
所以,可以把所有等待都移到起点处,解不会变差。
由于是环,破环为链处理。
如果在\(s\)时间在\(i (1<=i<=n)\)出发,那么到\(j (i<=j<i+n)\)的时间为\(s+(j-i)\)。根据要求,得\(s+(j-i)>=T_j\)
\(s=max(T_j-j)+i (i<=j<i+n)\)。总时间为\(max(T_j-j)+i+n-1 (i<=j<i+n)\)
就是要找一个\(i (1<=i<=n)\),使得\(max(T_j-j)+i+n-1 (i<=j<i+n)\)最小。
\(A_j=T_j-j\),则式子变为\(max(A_j)+i+n-1 (i<=j<i+n)\)
这样就得出的答案的式子。
发现\(max(A_j)+i+n-1 (i<=j<i+n)\)等于\(max(max(A_j) (i<=j<=n),max(A_j) (n+1<=j<i+n)+i+n-1\)
而由于\(A\)的后半段是由前半段复制过来并减\(n\)
所以\(max(A_j) (i+1<=j<=n+n)<max(max(A_j) (i<=j<=n)\)
所以式子可以变为\(max(A_j)+i+n-1 (i<=j<=n+n)\)(即后缀最值)。
但是有修改,这样还是不好维护答案。增加很好做,区间覆盖即可,但因为有减小的操作,减小后,修改的段会很多,无法处理。
我们发现,在\(max(A_j)\)一定时,\(i\)越小越好。
所以维护所有让\(max(A_j)\)变大的\(j\),就是从后往前的单调递增序列。
设这个序列为\(W_i\),那么若\(i={W_x}+1\),则\(max(A_j)=A_{W_{i+1}}\)
这样,式子变为\(A_{W_{i+1}}+{W_i}+1+i+n-1\),即\(A_{W_{i+1}}+{W_i}+i+n\)
单调递增序列可以用线段树维护,方法如下:
首先,单调递增序列只是用于合并答案,所以保存\(W\)的第一个和最后一个,还有这段序列的答案就行。
维护每个节点的\(max\),单调递增序列,还有整体考虑时左子节点的单调递增序列。
设计一个\(getst(l,r,x)\)表示求\(l~r\)的元素只考虑>\(x\)的答案。
若右儿子的最值不大于x,则不用考虑右儿子,只考虑左儿子即可。
否则,左儿子的递增序列一定完全包含在答案中,只考虑右儿子即可。
考虑\(pushup\)操作的实现:只要用左儿子的\(getst\)加上右儿子即可。
每次\(pushup\),时间复杂度为\(O(logn)\),总时间复杂度为\(O(nlog^2n)\)
代码还不算太难写,主要是推导比较复杂。

#include <stdio.h> 
#define max(a, b)(a > b ? a: b)
#define min(a, b)(a < b ? a: b) 
int sz[200010],inf = 999999999;
struct SJd {
	int x,y,mi;
	SJd() {}
	SJd(int a) {
		x = y = a;
		mi = inf;
	}
	SJd(int l, int r, int w) {
		x = y = -1;
		mi = inf;
		for (int i = r; i >= l; i--) {
			if (sz[i] > sz[w]) {
				if (y == -1) x = y = i;
				else x = i;
				mi = min(mi, sz[w] + i);
				w = i;
			}
		}
	}
};
SJd operator + (SJd a, SJd b) {
	if (a.x == -1) return b;
	if (b.x == -1) return a;
	SJd rt;
	rt.x = a.x;
	rt.y = b.y;
	rt.mi = min(a.mi, b.mi);
	rt.mi = min(rt.mi, sz[b.x] + a.y);
	return rt;
}
SJd jd[400010],zz[400010];
int ma[400010],wz[400010];
SJd getst(int i, int l, int r, int w) {
	if (r - l <= 4) return SJd(l, r - 1, w);
	int m = (l + r) >> 1;
	if (ma[(i << 1) | 1] <= sz[w]) return getst(i << 1, l, m, w);
	else return zz[i] + getst((i << 1) | 1, m, r, w);
}
void pushup(int i, int l, int r) {
	ma[i] = max(ma[i << 1], ma[(i << 1) | 1]);
	if (ma[i] == ma[i << 1]) wz[i] = wz[i << 1];
	else wz[i] = wz[(i << 1) | 1];
	int m = (l + r) >> 1;
	zz[i] = getst(i << 1, l, m, jd[(i << 1) | 1].x);
	jd[i] = zz[i] + jd[(i << 1) | 1];
}
void getddz(int i, int l, int r) {
	ma[i] = sz[l];
	wz[i] = l;
	jd[i] = SJd(l);
}
void jianshu(int i, int l, int r) {
	if (l + 1 == r) {
		getddz(i, l, r);
		return;
	}
	int m = (l + r) >> 1;
	jianshu(i << 1, l, m);
	jianshu((i << 1) | 1, m, r);
	pushup(i, l, r);
}
void xiugai(int i, int l, int r, int j) {
	if (l + 1 == r) {
		getddz(i, l, r);
		return;
	}
	int m = (l + r) >> 1;
	if (j < m) xiugai(i << 1, l, m, j);
	else xiugai((i << 1) | 1, m, r, j);
	pushup(i, l, r);
}
void xiugai(int x, int y, int n) {
	sz[x] = y - x;
	sz[x + n] = y - (x + n);
	xiugai(1, 1, n + 1, x);
}
int getans(int n) {
	SJd rt;
	rt.mi = ma[1];
	rt.x = wz[1] + n;
	rt.y = -1;
	rt = getst(1, 1, n + 1, wz[1] + n) + rt;
	return rt.mi + n;
}
int main() {
	int n,m,p;
	scanf("%d%d%d", &n, &m, &p);
	for (int i = 1; i <= n; i++) {
		int a;
		scanf("%d", &a);
		sz[i] = a - i;
		sz[i + n] = a - (i + n);
	}
	jianshu(1, 1, n + 1);
	int la = getans(n);
	printf("%d\n", la);
	for (int i = 0; i < m; i++) {
		int x,y;
		scanf("%d%d", &x, &y);
		if (p == 1) {
			x ^= la;
			y ^= la;
		}
		xiugai(x, y, n);
		la = getans(n);
		printf("%d\n", la);
	}
	return 0;
}
posted @ 2019-08-12 20:47  lnzwz  阅读(178)  评论(0编辑  收藏  举报