P6109 [Ynoi2009] rprmq1

lalaouye發表於2024-10-04

優美的資料結構題。

這題先修改再查詢,基本明確了要使用掃描線做這道題。

我們將第一維視為時間,那麼我們對於一個操作,將其變為時刻 \(l_1\) 時,在區間 \([l_2,r_2]\) 加上 \(x\),時刻 \(r_1+1\) 時,在區間 \([l_2,r_2]\) 減去 \(x\)

然後對於一個查詢,相當於是要求區間 \([l_2,r_2]\) 在時刻 \([l_1,r_1]\) 中的歷史版本和。直接維護非常的不優秀啊,考慮最佳化這個東西。

繼續將問題簡化,如果詢問只有一段字首,也就是詢問都為以 \((1,r_1,l2,r2)\) 形式出現的該怎麼做?

我們可以求歷史版本和,將操作和詢問掛在時刻上,邊修改邊查詢即可。

而對於一段字首,我們也可以用類似的辦法,先完成所有時刻的操作,再開始求歷史版本和,從時刻 \(n\)\(1\),邊修改邊查詢,這樣就可以完成一段字尾。

那麼接下來啟發我們對於一個查詢將其劈成兩半,分別查詢。具體我們可以考慮取若干個中點,解決若干次詢問的查詢。

我們可以進行線段樹分治,將區間掛在最小的能包含自己的線段樹上,其實就是 \(l\le l_1\le mid< r_1\le r\)\(l,r,mid\) 分別為線段樹節點的左節點,右節點,和中點,對於每個節點,每次先將 \([l,mid]\) 時刻的操作先完成,再進入線段樹右節點,然後再處理掛在該節點上的詢問的後半段,開始查詢歷史版本和,處理完這些詢問後我們減去 \([mid+1,r]\) 的貢獻,再處理詢問的前半段,邊查詢邊回撤 \([l,mid]\) 的操作,最後再進入左節點。這樣,就能優美地處理完了所有詢問。

最後講講線段樹維護細節吧。開始查詢歷史版本和,其本質是讓所有節點的維護歷史版本的節點都等於當前的值,我們可以在要查詢時打一個標記,但是標記是不能亂打的!如果我們像普通標記下傳那樣,就會存在一個區間加的標記與開始查詢的標記產生衝突的問題,不信自己去分情況手玩一下,本人因為這個調了很久!這個時候我們可以在每次開始查詢標記下傳時先把區間加的標記下傳就沒問題了。

哦對了,注意是歷史版本和,所以對於每個時刻一定要先將加的值小的操作先處理!

時間複雜度 \(\mathcal{O}(n\log^2n)\)

程式碼:

#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define id(x, y) n * (x - 1) + y
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 5e5 + 5, M = (1ll << 31) - 1, P = 998244353, inf = 1e16;
constexpr double PI = acos (-1.0);
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int mx[N], his[N];
bool sta[N];
int tag[N][3];
bool cov[N];
void add (int p, int k1, int k2) {
  his[p] = max (his[p], mx[p] + k2);
  mx[p] += k1;
  tag[p][1] = max (tag[p][1], tag[p][0] + k2);
  tag[p][0] += k1;
}
void cover (int p) {
  add (ls, tag[p][0], tag[p][1]);
  add (rs, tag[p][0], tag[p][1]);
  tag[p][0] = tag[p][1] = 0;
  tag[p][2] = 1;
  his[p] = mx[p], tag[p][1] = tag[p][0];
}
void psd (int p) {
  if (tag[p][2]) {
    cover (ls), cover (rs);
    tag[p][2] = 0;
  }
  add (ls, tag[p][0], tag[p][1]);
  add (rs, tag[p][0], tag[p][1]);
  tag[p][0] = tag[p][1] = 0;
}
void upd (int p, int l, int r, int L, int R, int k) {
  
  if (L <= l && r <= R) {
    add (p, k, k);
    return ;
  } int mid = l + r >> 1; psd (p);
  if (L <= mid) upd (ls, l, mid, L, R, k);
  if (R > mid) upd (rs, mid + 1, r, L, R, k);
  mx[p] = max (mx[ls], mx[rs]);
  his[p] = max (his[ls], his[rs]);
}
int qry (int p, int l, int r, int L, int R) {
  if (L <= l && r <= R) return his[p];
  int ret = 0, mid = l + r >> 1;
  psd (p);
  if (L <= mid) ret = max (ret, qry (ls, l, mid, L, R));
  if (R > mid) ret = max (ret, qry (rs, mid + 1, r, L, R));
  return ret;
}
int n, m, q;
class oper {
  public:
  int l, r, k;
  friend bool operator < (const oper &a, const oper &b) {
    return a.k < b.k;
  }
} ;
vector <oper> vec[50005], vvc[50005];
int ans[N];
class ask {
  public:
    int l1, r1, l2, r2, i; 
} ;
bool cmp1 (ask o, ask p) {
  return o.r1 < p.r1;
}
bool cmp2 (ask o, ask p) {
  return o.l1 > p.l1;
}
vector <ask> vc[N];
void insert (int p, int l, int r, ask x) {
  if (l == r) {
    vc[p].eb (x);
    return ;
  }
  int mid = l + r >> 1;
  if (x.r1 <= mid) insert (ls, l, mid, x);
  else if (x.l1 > mid) insert (rs, mid + 1, r, x);
  else vc[p].eb (x);
}
void solve (int p, int l, int r) {
  if (l == r) {
    if (! vc[p].size ()) return ;
    for (auto it : vec[l]) upd (1, 1, n, it.l, it.r, it.k);
    cover (1); 
    for (auto it : vc[p]) {
      ans[it.i] = qry (1, 1, n, it.l2, it.r2);
    }
    for (auto it : vec[l]) upd (1, 1, n, it.l, it.r, - it.k);
    return ;
  }
  int mid = l + r >> 1;
  rep (i, l, mid) {
    for (auto it : vec[i]) {
      int L = it.l, R = it.r, k = it.k;
      upd (1, 1, n, L, R, k);
    }
  }
  solve (rs, mid + 1, r);
  for (auto it : vc[p]) {
    vvc[it.r1].eb ((oper) {it.l2, it.r2, it.i});
  }
  rep (i, mid + 1, r) {
    for (auto it : vec[i]) {
      int L = it.l, R = it.r, k = it.k;
      upd (1, 1, n, L, R, k);
    }
    if (i == mid + 1) cover (1);
    for (auto it : vvc[i]) {
      ans[it.k] = max (ans[it.k], qry (1, 1, n, it.l, it.r));
    } vvc[i].clear ();
  }
  rep (i, mid + 1, r) {
    for (auto it : vec[i]) {
      int L = it.l, R = it.r, k = it.k;
      upd (1, 1, n, L, R, - k);
    }
  }
  for (auto it : vc[p]) {
    vvc[it.l1].eb ((oper) {it.l2, it.r2, it.i});
  }
  cover (1);
  rrp (i, l, mid) {
    for (auto it : vvc[i]) {
      ans[it.k] = max (ans[it.k], qry (1, 1, n, it.l, it.r));
    }
    for (int j = vec[i].size (); j >= 0; -- j) {
      if (j == vec[i].size ()) continue;
      int L = vec[i][j].l, R = vec[i][j].r, k = vec[i][j].k;
      upd (1, 1, n, L, R, - k);
      // if(p==3)
      // cout<<L<<" "<<R<<" "<<-k<<" "<<i<<" "<<his[1]<<endl;
    } vvc[i].clear ();
  }
  solve (ls, l, mid);
}
signed main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd (), m = rd (), q = rd (); ++ n;
  rep (i, 1, m) {
    int l1 = rd (), l2 = rd (), r1 = rd (), r2 = rd (), x = rd ();
    vec[l1].eb ((oper) {l2, r2, x}), vec[r1 + 1].eb ((oper) {l2, r2, - x});
  }
  rep (i, 1, n) sort (vec[i].begin (), vec[i].end ());
  rep (i, 1, q) {
    int l1 = rd (), l2 = rd (), r1 = rd (), r2 = rd ();
    insert (1, 1, n, (ask) {l1, r1, l2, r2, i});
  }
  solve (1, 1, n);
  rep (i, 1, q) printf ("%lld\n", ans[i]);
}