「AHOI / HNOI2018」转盘(找性质+线段树维护单调栈)

https://loj.ac/problem/2495

考虑最优的决策,有一种是一直往右走,不在一个点上等,为什么是对的呢?

证明:如果在一个点上等一刻,不如把起点往前移一位。

破环为链复制一遍以方便记答案。

首先需要的时间一定(ge n-1),不妨枚举倒数第(n)个点是(i),再考虑走到(i)的时间最小是(t)才能在剩下(n-1)步标记所有点。

写成式子就是(forall jin[i,i+n-1] t+i-j>=t[j])
(t=max(t[j]+j-i)(jin[i,i+n-1]))

不难发现(jin [i,i+n-1])的限制可以改为(jin [i,2n]),因为(t[j+n]-(j+n)< t[j]-j)

写成总式子:
(Ans=n+1+min_{i=1}^n i+max_{j=1}^{2n}t[j]-j)
(t[j]-j)看成整体,相当于顺着做一个递减的单调栈,顺便统计一下答案。

用《楼房重建》那种线段树维护即可。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;

const int N = 2e5 + 5;

#define i0 i + i
#define i1 i + i + 1

int n, m, tp;

int a[N], x, y;

int t[N * 4], fx[N * 4];
int query(int i, int x, int y, int p) {
	if(x == y) {
		return x <= n ? max(t[i], p) + x : 1e9;
	}
	int m = x + y >> 1;
	if(p > t[i1]) {
		int v = (m + 1 <= n) ? m + 1 + p : 1e9;
		return min(v, query(i0, x, m, p));
	} else {
		return min(fx[i], query(i1, m + 1, y, p));
	}
}
int pl, pr, px;
void add(int i, int x, int y) {
	if(y < pl || x > pr) return;
	if(x == y) {
		t[i] = px; return;
	}
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
	t[i] = max(t[i0], t[i1]);
	fx[i] = query(i0, x, m, t[i1]);
}

void xiu(int x) {
	pl = pr = x, px = a[x] - x;
	add(1, 1, 2 * n);
	pl = pr = x + n, px = a[x] - (x + n);
	add(1, 1, 2 * n);
}

int main() {
	scanf("%d %d %d", &n, &m, &tp);
	fo(i, 1, n) {
		scanf("%d", &a[i]);
		xiu(i);
	}
	int ans = query(1, 1, 2 * n, -1e9) + n - 1;
	pp("%d
", ans);
	fo(ii, 1, m) {
		scanf("%d %d", &x, &y);
		if(tp) x ^= ans, y ^= ans;
		a[x] = y;
		xiu(x);
		ans = query(1, 1, 2 * n, -1e9) + n - 1;
		pp("%d
", ans);
	}
}
原文地址:https://www.cnblogs.com/coldchair/p/12690076.html