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;
}

启示: 不要老是想分块,说不定线段树也能维护对吧。还有=一定要好好观察性质,不要上来就直接维护。

posted @ 2025-09-03 18:50  Little_corn  阅读(12)  评论(0)    收藏  举报