[HNOI/AHOI2018]转盘
洛谷传送门
BZOJ传送门
题目描述
一次小G和小H准备去聚餐,但是由于太麻烦了于是题面简化如下:
一个转盘上有摆成一圈的 n n 个物品(编号 ),其中的 i i 个物品会在 时刻出现。
在 0 0 时刻时,小G可以任选 个物品中的一个,我们将其编号为 s0 s 0 。并且如果 i i 时刻选择了物品 ,那么 i+1 i + 1 时刻可以继续选择当前物品或选择下一个物品。当 si s i 为 n n 时,下一个物品为物品 ,否则为物品 si+1 s i + 1 。在每一时刻(包括 0 0 时刻),如果小G选择的物品已经出现了,那么小G将会标记它。小H想知道,在物品选择的最优策略下,小G什么时候能标记所有物品?
但麻烦的是,物品的出现时间会不时修改。我们将其描述为 次修改,每次修改将改变其中一个物品的出现时间。每次修改后,你也需求出当前局面的答案。对于其中部分测试点,小H还追加了强制在线的要求。
输入输出格式
输入格式:
第一行三个非负整数 n n 、 、 p p ,代表一共有 个物品, m m 次修改。 只有 0 0 或两种取值,强制在线时 p p 为,否则 p p 为.
接下来一行,有 n n 个非负整数,第 个数 Ti T i 代表物品 i i 的出现时间。
接下来 行,每行两个非负整数 x x 、 ,代表一次修改及询问。修改方式如下:
(1)如果 p=0 p = 0 ,则表示物品 x x 的出现时间 修改为 y y 。
(2)如果 ,在先将 x x 和 分别异或 LastAns L a s t A n s ,得到 x′ x ′ 和 y′ y ′ ,然后将物品 x′ x ′ 的出现时间 Tx′ T x ′ 修改为 y′ y ′ 。其中的 LastAns L a s t A n s 是前一个询问的结果;特别的,第一次修改时 LastAns L a s t A n s 为初始局面的答案。
保证输入合法。
输出格式:
第一行一个整数,代表初始局面的答案。
接下来 m m 行每行一个整数,分别代表每次修改后的答案。
输入输出样例
输入样例#1:
5 3 0
1 2 3 4 5
3 5
5 0
1 4
输出样例#1:
5
7
6
7
说明
【数据范围】
。
解题分析
有个很显然的结论:我们肯定不会停在原地。
似乎不是很显然, 但我们可以倒着想:
一开始你在 t t 时刻, 你可以选择停留在原地或向前走一步, 然后。
每个点上的物品会在 Ti T i 时消失, 求能得到所有物品的一个最小的 t t 。
显然这样考虑我们就不可能停下来了…..
考虑破环为链, 倍长数组。 我们在
[n,2n−1]
[
n
,
2
n
−
1
]
位置枚举一个终点
i
i
, 那么要求是:
最终答案为
这样就有了一个 O(n2log(N)) O ( n 2 l o g ( N ) ) 的优秀做法。
把
i
i
中间的提出来, 设
Ai=Ti−i
A
i
=
T
i
−
i
,就有:
因为 Ai=Ti−i>Ti−(i+n)=Ai+n A i = T i − i > T i − ( i + n ) = A i + n , 所以我们可以减少一些限制:
这样等于我们只需要一个后缀的最大值就可以了, 但是并没有优化复杂度。
建立线段树, 设节点 P P 管辖范围为, ansP=minmidi=L(maxRj=i(Ai)+i) a n s P = m i n i = L m i d ( m a x j = i R ( A i ) + i ) , mxP=maxRi=L(Ai) m x P = m a x i = L R ( A i ) 。
当 L=R L = R 时, ansP=Ti,mxP=Ai a n s P = T i , m x P = A i 。
现在考虑如何合并两个区间。
显然靠右的一个区间的最大值一定会被计算入左边的这一部分, 设与
P
P
合并的节点为,因此实际上我们求的是:
用 query(L,R,mxH) q u e r y ( L , R , m x H ) 表示这一过程, 那么:
- 当 L=R L = R 时, ansfat[P]=max(AL,mxH)+L a n s f a t [ P ] = m a x ( A L , m x H ) + L
- 当 mxrs[P]≥mxH m x r s [ P ] ≥ m x H 时, 左半部分无关, 所以 ansfat[P]=min(ansP,query(mid+1,R,mxH)) a n s f a t [ P ] = m i n ( a n s P , q u e r y ( m i d + 1 , R , m x H ) )
- 否则当 i∈[mid+1,R] i ∈ [ m i d + 1 , R ] 时答案为 mid+1+mxH m i d + 1 + m x H ,另一部分递归到左区间处理, ansfat[P]=min(mid+1+mxH,query(L,mid,mxH)) a n s f a t [ P ] = m i n ( m i d + 1 + m x H , q u e r y ( L , m i d , m x H ) ) 。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 200050
#define ls (now << 1)
#define rs (now << 1 | 1)
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int T[MX], A[MX], dot, q, typ, lastans, bd;
struct Node {int mx, ans;} tree[MX << 2];
namespace SGT
{
int query(R int now, R int lef, R int rig, R int lim)
{
if(lef == rig) return lef + std::max(lim, A[lef]);
int mid = lef + rig >> 1;
if(tree[rs].mx >= lim) return std::min(tree[now].ans, query(rs, mid + 1, rig, lim));
return std::min(query(ls, lef, mid, lim), mid + 1 + lim);
}
IN void pushup(R int now, R int lef, R int rig)
{
tree[now].mx = std::max(tree[ls].mx, tree[rs].mx);
tree[now].ans = query(ls, lef, lef + rig >> 1, tree[rs].mx);
}
void build(R int now, R int lef, R int rig)
{
if(lef == rig) return tree[now].ans = T[lef], tree[now].mx = A[lef], void();
int mid = lef + rig >> 1;
build(ls, lef, mid), build(rs, mid + 1, rig);
pushup(now, lef, rig);
}
void modify(R int now, R int lef, R int rig, R int tar)
{
if(lef == rig) return tree[now].ans = T[lef], tree[now].mx = A[lef], void();
int mid = lef + rig >> 1;
if(tar > mid) modify(rs, mid + 1, rig, tar);
else modify(ls, lef, mid, tar);
pushup(now, lef, rig);
}
}
int main(void)
{
int a, b;
in(dot), in(q), in(typ); bd = dot << 1;
for (R int i = 1; i <= dot; ++i) in(T[i]), A[i] = T[i] - i;
for (R int i = dot + 1; i <= bd; ++i) T[i] = T[i - dot], A[i] = A[i - dot] - dot;
SGT::build(1, 1, bd); printf("%d\n", lastans = tree[1].ans + dot - 1);
W (q--)
{
in(a), in(b); if(typ) a ^= lastans, b ^= lastans;
T[a] = T[a + dot] = b;
A[a] = T[a] - a; A[a + dot] = A[a] - dot;
SGT::modify(1, 1, bd, a), SGT::modify(1, 1, bd, a + dot);
printf("%d\n", lastans = tree[1].ans + dot - 1);
}
}