[Luogu P4425] [BZOJ 5286] [HNOI AHOI2018]转盘

[HNOI/AHOI2018]转盘

洛谷传送门
BZOJ传送门

题目描述

一次小G和小H准备去聚餐,但是由于太麻烦了于是题面简化如下:

一个转盘上有摆成一圈的 n n 个物品(编号1n ),其中的 i i 个物品会在 Ti 时刻出现。

0 0 时刻时,小G可以任选 n 个物品中的一个,我们将其编号为 s0 s 0 。并且如果 i i 时刻选择了物品 si ,那么 i+1 i + 1 时刻可以继续选择当前物品或选择下一个物品。当 si s i n n 时,下一个物品为物品 1 ,否则为物品 si+1 s i + 1 。在每一时刻(包括 0 0 时刻),如果小G选择的物品已经出现了,那么小G将会标记它。小H想知道,在物品选择的最优策略下,小G什么时候能标记所有物品?

但麻烦的是,物品的出现时间会不时修改。我们将其描述为 m 次修改,每次修改将改变其中一个物品的出现时间。每次修改后,你也需求出当前局面的答案。对于其中部分测试点,小H还追加了强制在线的要求。

输入输出格式

输入格式:

第一行三个非负整数 n n m p p ,代表一共有 n 个物品, m m 次修改。 p 只有 0 0 1两种取值,强制在线时 p p 1,否则 p p 0.

接下来一行,有 n n 非负整数,第 i 个数 Ti T i 代表物品 i i 的出现时间。

接下来 m 行,每行两个非负整数 x x y ,代表一次修改及询问。修改方式如下:

(1)如果 p=0 p = 0 ,则表示物品 x x 的出现时间 Tx 修改为 y y

(2)如果 p=1 ,在先将 x x y 分别异或 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

说明

【数据范围】

img

3n105,0m105,0Ti/Tx105

解题分析

有个很显然的结论:我们肯定不会停在原地。

似乎不是很显然, 但我们可以倒着想:

一开始你在 t t 时刻, 你可以选择停留在原地或向前走一步, 然后t

每个点上的物品会在 Ti T i 时消失, 求能得到所有物品的一个最小的 t t

显然这样考虑我们就不可能停下来了…..

考虑破环为链, 倍长T数组。 我们在 [n,2n1] [ n , 2 n − 1 ] 位置枚举一个终点 i i , 那么要求是:

t(ij)Tj(j[in+1,i])tmin=maxj=in+1i(Tjj)+i

最终答案为

Ans=min2n1i=n(maxij=in+1(Tjj)+i) A n s = m i n i = n 2 n − 1 ( m a x j = i − n + 1 i ( T j − j ) + i )

这样就有了一个 O(n2log(N)) O ( n 2 l o g ( N ) ) 的优秀做法。

i i 中间的n提出来, 设 Ai=Tii A i = T i − i ,就有:

Ans=minni=1(maxi+n1j=i(Ai)+i)+N1 A n s = m i n i = 1 n ( m a x j = i i + n − 1 ( A i ) + i ) + N − 1

因为 Ai=Tii>Ti(i+n)=Ai+n A i = T i − i > T i − ( i + n ) = A i + n , 所以我们可以减少一些限制:
Ans=minni=1(max2nj=i(Ai)+i)+N1 A n s = m i n i = 1 n ( m a x j = i 2 n ( A i ) + i ) + N − 1

这样等于我们只需要一个后缀的最大值就可以了, 但是并没有优化复杂度。

建立线段树, 设节点 P P 管辖范围为[L,R] 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 合并的节点为H,因此实际上我们求的是:

ansfat[P]=minRi=L(max(maxRj=i(Ai),mxH)+i) a n s f a t [ P ] = m i n i = L R ( m a x ( m a x j = i R ( A i ) , m x H ) + i )

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);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值