[Balkan2007]Mokia

知识点: CDQ分治

原题面 Luogu darkbzoj


套路题,20 min A 了。
什么嘛,我写的还是挺快的嘛(


题意简述

给定一初始全为 \(0\) 的 \(w\times w\) 的格子图。
有若干次操作,每次操作是下列形式之一:

  1. 增加某格子的权值,共有 \(m\) 次。
  2. 询问某子矩阵的权值和,共有 \(q\) 次。

\(1\le w\le 2\times 10^6\),\(1\le m\le 1.6\times 10^5\),\(1\le q\le 10^4\)。


分析题意

套路题,考虑二维前缀和的形式,容斥一下将询问矩阵拆成四个子询问矩阵。
问题变为对于每一个询问 \((x_i,y_i)\) 统计时间先于它的,在其左下方的点的数量。
显然的三维偏序形式,Cdq 直接搞即可。


一些细节

拆分询问后,把答案存在四个询问中时间最小的上。
cdq 打乱了原有时间顺序,为方便输出,代码中记录了时间戳。


代码实现

//知识点:CDQ 分治 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 2e6 + 10;
//=============================================================
struct Data {
  int type, t, x, y, a;
} a[kMaxn], tmp[kMaxn];
int w, n, ans[kMaxn];
bool vis[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
namespace TreeArray {
  #define lowbit(x) (x&-x)
  const int kMaxm = 2e6 + 10;
  int time, ti[kMaxn], t[kMaxn];
  void Clear() {
    time ++;
  }
  void Add(int pos_, int val_) {
    for (; pos_ <= w; pos_ += lowbit(pos_)) {
      if (ti[pos_] < time) {
        t[pos_] = 0;
        ti[pos_] = time;
      }
      t[pos_] += val_;
    }
  }
  int Sum(int pos_) {
    int ret = 0;
    for (; pos_; pos_ -= lowbit(pos_)) {
      if (ti[pos_] < time) {
        t[pos_] = 0;
        ti[pos_] = time;
      }
      ret += t[pos_];
    }
    return ret;
  }
}
#define mid ((l_+r_)>>1)
void Cdq(int l_, int r_) {
  if (l_ == r_) return ;
  Cdq(l_, mid), Cdq(mid + 1, r_);
  int p1 = l_, p2 = mid + 1, p = l_ - 1;
  for (; p2 <= r_; ++ p2) {
    for (; p1<= mid && a[p1].x <= a[p2].x; ++ p1) {
      tmp[++ p] = a[p1];
      if (! a[p1].type) TreeArray :: Add(a[p1].y, a[p1].a);
    }
    tmp[++ p] = a[p2];
    if (! a[p2].type) continue ;
    ans[a[p2].t] += a[p2].type * TreeArray :: Sum(a[p2].y);
  }
  for (; p1 <= mid; ++ p1) tmp[++ p] = a[p1];
  for (int i = l_; i <= r_; ++ i) a[i] = tmp[i];
  TreeArray :: Clear();
}
//=============================================================
int main() {
  while (true) {
    int opt = read();
    if (opt == 3) break;
    if (opt == 0) w = read();
    if (opt == 1) {
      a[++ n] = (Data) {0, n, read(), read(), read()};
    }
    if (opt == 2) {
      int x1 = read(), y1 = read(), x2 = read(), y2 = read();
      vis[n + 1] = true;
      a[++ n] = (Data) {1, n, x2, y2, 0};
      a[++ n] = (Data) {- 1, n - 1, x1 - 1, y2, 0};
      a[++ n] = (Data) {- 1, n - 2, x2, y1 - 1, 0};
      a[++ n] = (Data) {1, n - 3, x1 - 1, y1 - 1, 0};
    }
  }
  Cdq(1, n);
  for (int i = 1; i <= n; ++ i) {
    if (vis[i]) printf("%d\n", ans[i]);
  }
  return 0;
}
上一篇:P1368 工艺 /【模板】最小表示法


下一篇:C语言判端三个数的大小并求出最大值