欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-28 12:49:15
题意 "题目链接" Sol 首先猜一个结论:对于每次询问,枚举一个起点然后不断等到某个点出现时才走到下一个点一定是最优的。 证明不会,考场上拍了3w组没错应该就是对的吧。。。 首先把数组倍长一下方便枚举起点,然后就是一个单调队列的模型了。整理一下我们需要求的东西就是这个 $$n 1 + \min_{ ......

题意

题目链接

sol

首先猜一个结论:对于每次询问,枚举一个起点然后不断等到某个点出现时才走到下一个点一定是最优的。

证明不会,考场上拍了3w组没错应该就是对的吧。。。

首先把数组倍长一下方便枚举起点,然后就是一个单调队列的模型了。整理一下我们需要求的东西就是这个

\[n - 1 + \min_{i=1}^n i + (\max_{j=i}^{2n} t[j] - j)\]

(\(t[j]\)表示第\(j\)个位置出现的时间,其实\(\max\)的上界应该是\(i + n - 1\)的,但是显然后面的部分都不会更优)

其中\(n-1\)\(t[j] - j\)可以直接算,这玩意儿可以用线段树维护。

对于每个区间维护\(mx[i]\)表示区间最大值,\(ans[i]\)表示在右区间的影响下,左区间的\(min(i + \max\text{后缀})\)

合并时考虑右区间对左区间的影响。

如果\(mx[rs] < mx[ls]\),我们可以直接用\(ans[ls]\)更新答案,然后递归\(ls(rs)\)

否则用mid更新一下答案然后递归\(ls(ls)\)

这样每次可以消除掉一半的区间,因此复杂度是\(o(nlog^2n)\)

最后询问的时候可以直接拿\([n + 1, 2n]\)的最大值去二分,实际上这部分的值就是\(max[1, n] - n\)

那么只需要维护\([1, n]\)的线段树就行了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10, inf = 1e9 + 7;
template<typename a, typename b> inline bool chmin(a &x, b y) {
    if(x > y) {x = y; return 1;}
    else return 0;
}
template<typename a, typename b> inline bool chmax(a &x, b y) {
    if(x < y) {x = y; return 1;}
    return 0;
}
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int n, m, p, t[maxn],  q[maxn];
int ans[maxn], mx[maxn];
#define ls k << 1
#define rs k << 1 | 1
int query(int k, int l, int r, int val) {
    if(l == r) return l + max(mx[k], val);
    int mid = l + r >> 1;
    if(val < mx[rs]) return min(ans[k], query(rs, mid + 1, r, val));
    else return min(mid + val + 1, query(ls, l, mid, val)); 
}
void update(int k, int l, int r) {
    mx[k] = max(mx[ls], mx[rs]);
    ans[k] = query(ls, l, (l + r) >> 1, mx[rs]);
}
void modify(int k, int l, int r, int p, int v) {
    if(l == r) {mx[k] = v; return ;}
    int mid = l + r >> 1;
    if(p <= mid) modify(ls, l, mid, p, v);
    else modify(rs, mid + 1, r, p, v);
    update(k, l, r);
}
void build(int k, int l, int r) {
    if(l == r) {mx[k] = t[l]; return ;}
    int mid = l + r >> 1;
    build(ls, l, mid); build(rs, mid + 1, r);
    update(k, l, r);
}
int main() {
    n = read(); m = read(); p = read();
    for(int i = 1; i <= n; i++) t[i] = read();
    for(int i = 1; i <= n; i++) t[i] -= i;
    build(1, 1, n); int las = 0;
    printf("%d\n", las = (query(1, 1, n, mx[1] - n) + n - 1));
    while(m--) {
        int x = read(), y = read();
        if(p) x ^= las, y ^= las;
        modify(1, 1, n, x, y - x);
        printf("%d\n", las = (query(1, 1, n, mx[1] - n) + n - 1));
    }
    return 0;
}
/*
5 0 0
1 2 5 4 0
*/