[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;
}