【题解】HNOI2018转盘

  何学长口中所说的‘一眼题’……然而实际上出出来我大HN全省也只有一个人A……

  首先我们需要发现一个性质:我们永远可以在最后一圈去标记所有的物品。倘若我们反复转圈,那么这完全是可以省下来的。所以我们破环为链,以\(S\) 物品作为第一个被访问的节点时所需要的时间就是 \( max(T_{x} - x) + S \),其中 \( S <= x <= S + n - 1\)。问题转化为如何使用数据结构来维护这些值得最小值。注意到 \(T_{x} - x \)都只与 \(x\) 有关,我们就他将它们合为一个变量 :\(A_{x} = T_{x} - x\)。由于 \(T_{x} == T_{x + n}\),所以 \(A_{x} > A_{x +n}\) 。所以若 \(A_{x} = max(A_{j}) (S <= j <= S + n - 1)\),那么 \(A_{x} = max(A_{j}) (S <= j <= 2 * n)\)。这里应该是一个启示吧,有时候观察数组的性质,不仅仅缩小范围是有效的,实际上扩大范围为前缀 / 后缀也一样是非常重要的一个方面。那么这个时候我们就发现:这实际上对于答案可能产生贡献的就是所有从后往前比任何一个元素都要大的 \(A_{x}\) 啦。

  使用线段树我们可以维护这样的一个单调栈,由于我们已经固定了 \(A_{x}\),我们只需要使得 \(S\) 最小即可。显然\(S\)尽量小就应该是 \(x\) 左侧第一个比它大的数的下标+1。使用线段树维护这个信息的最大值即可(楼房重建问题)。

  其中 \(mx\) 维护区间内 \(A_{x}\) 的最大值,\(mn\) 维护区间内 \(A_{x} + S\) 的最小值, \(rec\) 记录区间内只考虑左边区间 & 右边区间最大值的贡献 (但考虑右边对于左边的限制) 。

 

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define INF 999999999
int n, m, P, N, ans, T[maxn];
int mn[maxn], mx[maxn], rec[maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

int Query(int p, int l, int r, int K)
{
    if(l == r)
    {
        if(mx[p] > K) return min(mn[p], K + l + 1);
        else return INF;
    }
    int mid = (l + r) >> 1;
    if(mx[p << 1 | 1] <= K) return Query(p << 1, l, mid, K);
    else return min(rec[p], Query(p << 1 | 1, mid + 1, r, K)); 
}

void Push_Up(int p, int l, int r)
{
    int ls = p << 1, rs = p << 1 | 1;
    mx[p] = max(mx[ls], mx[rs]);
    int mid = (l + r) >> 1;
    mn[p] = min(rec[p] = Query(ls, l, mid, mx[rs]), mn[p << 1 | 1]);
}

void Build(int p, int l, int r)
{
    if(l == r)
    {
        mx[p] = T[l] - l; mn[p] = T[l];
        return; 
    }
    int mid = (l + r) >> 1;
    Build(p << 1, l, mid); Build(p << 1 | 1, mid + 1, r);
    Push_Up(p, l, r); 
}

void Update(int p, int l, int r, int x)
{
    if(l == r)
    {
        mx[p] = T[l] - l; mn[p] = T[l];
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) Update(p << 1, l, mid, x);
    else Update(p << 1 | 1, mid + 1, r, x);
    Push_Up(p, l, r);
}

int main()
{
    n = read(), m = read(), P = read(), N = 2 * n;
    for(int i = 1; i <= n; i ++) T[i] = read();
    for(int i = 1; i <= n; i ++) T[i + n] = T[i];
    Build(1, 1, N); 
    printf("%d\n", ans = min(mx[1] + 1, rec[1]) + n - 1);
    for(int i = 1; i <= m; i ++)
    {
        int x = read(), y = read();
        if(P) x ^= ans, y ^= ans;
        T[x] = T[x + n] = y; 
        Update(1, 1, N, x); Update(1, 1, N, x + n);
        printf("%d\n", ans = min(mx[1] + 1, rec[1]) + n - 1);
    }
    return 0;
}

 

posted @ 2018-08-22 00:29  Twilight_Sx  阅读(194)  评论(0编辑  收藏  举报