版权声明:_ https://blog.csdn.net/lunch__/article/details/84671298
题意
- 一个转盘上有 个物品,每个物品有一个出现时间,你每次可以选择一个位置,每次停在原地或者向前走一格,如果当前时刻大于等于物品出现时间你就可以标记这个物品,问标记物品最少要多久, 次修改
我杀我自己… 做题效率极低抄题解都抄不懂…
首先发现一个性质
一定存在一种最优方案是在起点停留一段时间后一直向前走
感性理解一下就是你走的最优路径是一圈多一点点
那么你在多出一点点的下一格停够时间后不会出现新的无法标记的物品
并且时间也不会更多,所以每一种方案都可以转化成这样
则
设 并且不看常数项
因为 不会对 造成影响
我们用一个线段树来维护这个式子里的值
为区间内 的最大值, 为所有包含了整个右区间的那个式子的最小值
就例如当前区间是 那
我们可以像楼房重建那道题一样, 的时候递归查询
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;
}