P5609 [Ynoi2013] 对数据结构的爱 做题记录
Statement
给出 \(n, p\) 和一个 \(n\) 个整数组成的序列 \(a_1, a_2, ..., a_n\)。其中令 \(f_i(x) = x + a_i - [x + a_i \ge p] \times p\)。现在有 \(Q\) 次询问 \((l, r, x)\),要求求出 \(f_r(f_{r - 1} (f_{r - 2}(... f_l(x))))\) 的值。
\(n \le 10^6, Q \le 2 \times 10^5, 1s\)。
Solution
考虑拿线段树维护这个东西,对于节点 \((u, l, r)\),维护 \(sum_u = \sum_{i = l}^r a_i\) 以及 \(f_{l ..., r}\) 的分界点 \(c_0, c_1 ..., c_{r - l + 1}\),其中 \(c_i\) 表示可以减去 \(i \times p\) 的最小 \(x\)。不难发现 \(c_i\) 具有单调性,那么求解答案容易做到 2log。难点在于 pushup。
我们考虑用 \(c_{ls, i}, c_{rs, j}\) 去更新 \(c_{u, i + j}\),首先有条件 \(sum_{ls} + c_{ls, i + 1} - 1 - i\times p \ge c_{rs, j}\)。求解这个似乎需要 1log ……吗?我们注意到若 \(i \gets i + 1\),变化量相当于 \(c_{ls, i + 2} - c_{ls, i + 1} - p\)。同时注意到我们至少需要一个 \(+p\) 才可以多一个 \(-p\),所以变化量其实恒为正数,那么我们可以使用双指针维护最大的 \(j\)。
然后考虑贡献:\(c_{u, i + j} \gets \max(c_{ls, i}, c_{rs, j} - sum_{ls} + i \times p)\)。考虑比较 \((i, j)\) 和 \((i + 1, j - 1)\) 的贡献,由于条件限制,一定有 \(sum_{ls} + c_{ls, i + 1} - i \times p > c_{rs, j}\)。那么 \(c_{ls, i + 1} > \max(c_{ls, i}, c_{rs, j} - sum_{ls} + i \times p)\),贡献一定不好。所以我们直接每次更新新加入的 \(j\) 即可。此时 pushup 做到了 \(O(len)\)。总时间复杂度 \(O(n \log n + m \log^2 n)\)。
qwq
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pb emplace_back
#define pir pair<int, ll>
#define fi first
#define se second
#define inv(x) qpow(x, mod - 2)
#define il inline
#define mkpir make_pair
#define ull unsigned long long
#define umap unordered_map
using namespace std;
const int N = 1e6 + 10, M = 2e5 + 10;
const ll mod = 998244353, INF = 1e18;
il ll qpow(ll x, ll y){
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
il void chkmin(ll& x, ll y){if(y < x) x = y;}
il void chkmax(ll& x, ll y){if(y > x) x = y;}
il void chkmin(int& x, int y){if(y < x) x = y;}
il void chkmax(int& x, int y){if(y > x) x = y;}
il void chkmod(ll& x){x = (x + mod) % mod;}
il void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0;}
il void MUL(ll& x, ll y){x = x * y % mod;}
//#define int long long
int n, m;
ll a[N], p, c[N * 30];
int fir[N << 2], now, siz[N << 2];
int id(int o, int x){return fir[o] + x;}
struct Segtree{
#define ls (o << 1)
#define rs (o << 1 | 1)
#define mid ((l + r) >> 1)
ll sum[N << 2];
void pushup(int o, int l, int r){
sum[o] = sum[ls] + sum[rs];
int j = -1; ll lsum = sum[ls];
int g = min(siz[ls] + siz[rs], r - l + 3); fir[o] = now; now += g;
for(int i = 1; i < g; i++) c[id(o, i)] = INF; siz[o] = g;
c[id(o, 0)] = -INF;
for(int i = 0; i < siz[ls]; i++){
if(c[id(ls, i)] == INF) break;
ll up = (i + 1 == siz[ls] ? INF : c[id(ls, i + 1)]);
if(j != -1) j--;
while(j + 1 < siz[rs] && lsum + up - 1 - 1ll * i * p >= c[id(rs, j + 1)]){
j++;
chkmin(c[id(o, i + j)], max(c[id(ls, i)], c[id(rs, j)] - sum[ls] + 1ll * i * p));
}
}
}
void build(int o, int l, int r){
if(l == r){
sum[o] = a[l];
fir[o] = now; siz[o] = 2; now += 2;
c[id(o, 0)] = (-INF); c[id(o, 1)] = (p - a[l]); return;
}
build(ls, l, mid); build(rs, mid + 1, r);
pushup(o, l, r);
}
ll getval(int o, ll x){
ll tim = upper_bound(c + fir[o], c + fir[o] + siz[o], x) - (c + fir[o]) - 1;
return x - tim * p + sum[o];
}
ll getval(int o, int l, int r, int s, int t, ll x){
if(s <= l && r <= t) return getval(o, x);
ll ret = x;
if(s <= mid) ret = getval(ls, l, mid, s, t, ret);
if(mid < t) ret = getval(rs, mid + 1, r, s, t, ret);
return ret;
}
}tr;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m >> p;
for(int i = 1; i <= n; i++) cin >> a[i];
tr.build(1, 1, n); ll lstans = 0;
while(m--){
int l, r; ll x; lstans = (lstans % n + n) % n;
cin >> l >> r >> x; l ^= lstans; r ^= lstans; x ^= lstans;
cout << (lstans = tr.getval(1, 1, n, l, r, x)) << "\n";
}
return 0;
}
启示: 不要老是想分块,说不定线段树也能维护对吧。还有=一定要好好观察性质,不要上来就直接维护。

浙公网安备 33010602011771号