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