[洛谷P4425][BZOJ5286][HNOI/AHOI2018]转盘(线段树)

Solution

  • 如果一种最优解是:在时刻\(t\)从位置\(x\)开始,从时刻\(t+1\)开始共有\(t1\)个时刻选择当前物品,有\(t2\)个时刻选择下一个物品,那么这和在时刻\(t+t1\)从位置\(x\)开始,从时刻\(t+1\)开始都不选择当前物品,实际上是等价的
  • 那么只要考虑不停留的情况,问题转化为选择一个最小的开始时间\(b_{x}(t≥0)\),使得从位置\(x\)开始,花\(n\)个时刻完成标记,答案即\(t+n-1\)
  • 首先,为了处理环的情况,令\(T\)数组倍长,即$$T_{i}=T_{i-n}(n+1≤i≤2n)$$
  • 因为要求满足$$t+i-x≥T_{i}(\forall i,x≤i≤x+n-1)$$
  • 所以\(bx\)即$$max_{i=x}^{x+n-1}T_{i}-i$$
  • 那么令\(a_{i}=T_{i}-i\)
  • 发现$$T_{i}=T_{i-n}(x+n≤i≤2n)$$
  • 说明$$a_{i}<a_{i-n}(x+n≤i≤2n)$$
  • 那么\(b_{x}\)可以改写为后缀最大值的形式:$$max_{i=x}^{2n}a_{i}(1≤x≤n)$$
  • 因为$$ans=min(b_{x}+x)$$
  • 所以如果对于\(l≤i≤r\)满足\(b_{i}\)全部相同,那么选\(l\)一定比选\([l+1,r]\)中的任何一个要优
  • 将所有这样区间\([l,r]\)中的\(l\)取出,就形成了一个关于\(a_{i}\)的单调下降子序列
  • 特别地,这个子序列的每一项\(d_{i}\)都要满足$$a_{j}<a_{i}(\forall j,i<j≤2n)$$
  • 其实只要最后一项满足上述条件即可,也就是对这个单调下降子序列的末项作了限制
  • 那么为了方便,从右到左维护一个单调上升子序列,也就是取出所有的\(r\)放入\(c\)数组,所有的\(c_{i}+1\)就是要选的\(l\),对答案的贡献即$$(1).min(a_{c_{i+1}}+c_{i}+1)(1≤i<m)$$
  • 特别地,由于\(x\)\(c\)的取值在\([1,n]\),所以当\(c_{m}!=n\)时,位置\(c_{m}+1\)对答案的贡献是$$(2).max_{i=n+1}^{2n}a_{i}$$
  • 即$$max_{i=1}^{n}a_{i}-n$$
  • 因为在此条件下,\(c_{m}+1,n,n+1\)必处于同一区间\([l,r]\)
  • \(c_{m}=n\)\((2)\)显然成立
  • 于是对\((1),(2),a_{c_{1}}+1\)取最小值即可
  • 维护线段树,记\(calc(l,r,v,p)\)为一个四元组,表示节点\(p\)(对应区间\([l,r]\)),末项\(>v\)的单调上升子序列(从右到左)的信息:$$(c_{1},c_{m},a_{c_{1}},min(a_{c_{i+1}}+c_{i}+1)(1≤i<m))$$
  • \(val[p]\)表示节点\(p\)对应区间的\(max(a_{i})\)
  • \(b[p]\)表示\(calc(l,mid,val[p3],p2)\)\(p2,p3\)为左右子节点
  • 修改回溯时要更新\(val\)\(b\)
  • 下面讨论如何计算\(calc(l,r,v,p)\)
  • \(val[p3]≤v\),那么这个子序列与\(p3\)无关,递归\(p2\)即可
  • 否则,\(p2\)对子序列的贡献依然是\(b[p]\)
  • 显然回溯时\(b[p2]\)会比\(calc(l,mid,val[p3],p2)\)先算
  • 时间复杂度\(O(nlog^2n)\)

Code

#include <bits/stdc++.h>

using namespace std;

#define p2 p << 1
#define p3 p << 1 | 1

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

const int e = 1e5 + 5, inf = 0x3f3f3f3f;
struct node
{
	int c1, cm, a1, res;
}b[e * 4];
int n, m, val[e * 4], a[e], op, ans;

inline node calc(int l, int r, int v, int p)
{
	if (l == r) return val[p] > v ? (node){l, r, val[p], inf} : (node){0, inf, inf, inf};
	int mid = l + r >> 1;
	if (val[p3] <= v) return calc(l, mid, v, p2);
	if (val[p2] <= val[p3]) return calc(mid + 1, r, v, p3);
	node lc = b[p], rc = calc(mid + 1, r, v, p3);
	if (!lc.c1) return rc;
	return (node){lc.c1, rc.cm, lc.a1, min(min(lc.res, rc.res), rc.a1 + lc.cm + 1)};
}

inline void update(int l, int r, int s, int v, int p)
{
	if (l == r)
	{
		val[p] = v;
		return;
	}
	int mid = l + r >> 1;
	if (s <= mid) update(l, mid, s, v, p2);
	else update(mid + 1, r, s, v, p3);
	val[p] = max(val[p2], val[p3]);
	b[p] = calc(l, mid, val[p3], p2);
}

inline void build(int l, int r, int p)
{
	if (l == r)
	{
		val[p] = a[l] - l;
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, p2);
	build(mid + 1, r, p3);
	val[p] = max(val[p2], val[p3]);
	b[p] = calc(l, mid, val[p3], p2);
}

int main()
{
	int i, x, y;
	read(n); read(m); read(op);
	for (i = 1; i <= n; i++) read(a[i]);
	build(1, n, 1);
	node z = calc(1, n, val[1] - n, 1);
	ans = min(z.res, min(z.a1 + 1, val[1] - n + z.cm + 1));
	ans += n - 1;
	printf("%d\n", ans);
	while (m--)
	{
		read(x);
		read(y);
		if (op)
		{
			x ^= ans;
			y ^= ans;
		}
		a[x] = y;
		update(1, n, x, y - x, 1);
		node z = calc(1, n, val[1] - n, 1);
		ans = min(z.res, min(z.a1 + 1, val[1] - n + z.cm + 1));
		ans += n - 1;
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2020-01-15 14:49  花淇淋  阅读(112)  评论(0编辑  收藏  举报