[线段树] [分段函数] P5609 [Ynoi2013] 对数据结构的爱
尝试更自然地叙述思路。唯一一处跳跃性步骤也是符合人类直觉的。
题意:给你长为
不妨从函数的视角出发,容易发现这是一个由若干段
由于
重点是怎么实现 push_up 函数。考虑朴素合并,对于左边区间
(为了方便,不妨放宽限制化区间为前缀,只不过贡献时与
合法
那么直接双指针即可,复杂度为
证明:等价于
总结:本题运用线段树维护分段函数,重点为合并儿子信息。需要一定的直觉。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define MIN(a, b) a = min(a, b)
const int N = 1e6 + 5, inf = 1e17, M = 3e7 + 5;
int n, m, p, a[N], now, l, r, lstans;
namespace SGT{
#define lt (u << 1)
#define rt (u << 1 | 1)
#define mid (l + r >> 1)
int s[N << 2], x[M], beg[N << 2], siz[N << 2], tot;
inline void build(int u, int l, int r){
beg[u] = tot, siz[u] = r - l + 3; tot += siz[u];
for(int i = 0; i <= r - l + 2; ++i) x[beg[u] + i] = inf;
if(l == r) {x[beg[u]] = -inf, x[beg[u] + 1] = p - a[l], s[u] = a[l]; return ;}
build(lt, l, mid), build(rt, mid + 1, r);
s[u] = s[lt] + s[rt];
int lsiz = mid - l + 1, rsiz = r - mid;
for(int i = 0, j = 0; i <= lsiz; ++i){
while(1){
MIN(x[beg[u] + i + j], max(x[beg[lt] + i], x[beg[rt] + j] - s[lt] + i * p));
++j;
if(j > rsiz) {j = rsiz; break;}
if(x[beg[lt] + i + 1] - 1 + s[lt] - i * p < x[beg[rt] + j]) {--j; break;}
}
}
}
inline void fid(int u, int l, int r, int ll, int rr){
if(ll <= l && r <= rr){
int val = upper_bound(x + beg[u], x + beg[u] + siz[u], now) - (x + beg[u]) - 1;
now = now + s[u] - val * p;
return ;
}
if(ll <= mid) fid(lt, l, mid, ll, rr);
if(rr > mid) fid(rt, mid + 1, r, ll, rr);
}
}using namespace SGT;
signed main(){
cin >> n >> m >> p;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
build(1, 1, n);
while(m--){
scanf("%lld%lld%lld", &l, &r, &now);
l ^= lstans, r ^= lstans, now ^= lstans;
assert(l <= r);
fid(1, 1, n, l, r);
printf("%lld\n", now);
lstans = (now % n + n) % n;
}
return 0;
}