Luogu P4425 [HNOI/AHOI2018]轉盤 線段樹
阿新 • • 發佈:2018-12-09
題意
- 一個轉盤上有 個物品,每個物品有一個出現時間,你每次可以選擇一個位置,每次停在原地或者向前走一格,如果當前時刻大於等於物品出現時間你就可以標記這個物品,問標記物品最少要多久, 次修改
我殺我自己… 做題效率極低抄題解都抄不懂…
首先發現一個性質
一定存在一種最優方案是在起點停留一段時間後一直向前走
感性理解一下就是你走的最優路徑是一圈多一點點
那麼你在多出一點點的下一格停夠時間後不會出現新的無法標記的物品
並且時間也不會更多,所以每一種方案都可以轉化成這樣
則
設 並且不看常數項
因為 不會對 造成影響
我們用一個線段樹來維護這個式子裡的值
為區間內 的最大值, 為所有包含了整個右區間的那個式子的最小值
就例如當前區間是 那
我們可以像樓房重建那道題一樣, 的時候遞迴查詢
int query(int bh, int l, int r, int x) {
if (l == r) return l + max(x, mx[bh]);//區間長為1就直接找到最大的
if (mx[rs] >= x) return min(ans[bh], query(rson, x));//合併的區間對當前整個區間無影響
return min(query(lson, x), mid + 1 + x);//有影響的話那麼貪心選最小的
}
複雜度
Codes
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, p;
struct Segment_Tree {
#define mid ((l + r) >> 1)
#define ls (bh << 1)
#define rs (ls | 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r
int mx[N << 2], ans[N << 2];
void pushup(int bh, int l, int r) {
mx[bh] = max(mx[ls], mx[rs]);
ans[bh] = query(lson, mx[rs]);
}
void update(int bh, int l, int r, int x, int y) {
if (l == r) mx[bh] = y - x, ans[bh] = y;
else {
if (x <= mid) update(lson, x, y);
else update(rson, x, y);
pushup(bh, l, r);
}
}
int query(int bh, int l, int r, int x) {
if (l == r) return l + max(x, mx[bh]);
if (mx[rs] >= x) return min(ans[bh], query(rson, x));
return min(query(lson, x), mid + 1 + x);
}
}T;
int main() {
//freopen("4425.in", "r", stdin);
//freopen("4425.out", "w", stdout);
int x, y, lastans;
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; ++ i) {
scanf("%d", &y);
T.update(1, 1, n << 1, i, y);
T.update(1, 1, n << 1, i + n, y);
}
printf("%d\n", lastans = T.ans[1] + n - 1);
while (m --) {
scanf("%d%d", &x, &y);
x ^= lastans * p, y ^= lastans * p;
T.update(1, 1, n << 1, x, y);
T.update(1, 1, n << 1, x + n, y);
printf("%d\n", lastans = T.ans[1] + n - 1);
}
return 0;
}