[bzoj3343]教主的魔法——分块

Brief description

给定一个数列,您需要支持一下两种操作:

  1. 给[l,r]同加一个数
  2. 询问[l,r]中有多少数字大于或等于v

Algorithm analyse

这个题一时想不到什么有效的数据结构,但是暴力法非常好想:一个\(\Theta(n)\)的暴力算法。
我们考虑分块做,不那么暴力。
把数据分为\(\sqrt n\)一份,那么对于每一个查询,我们都可以把这个查询分为\(\sqrt n\)个区间,修改的时候也是\(\Theta(\sqrt n)\)的级别,所以总的复杂度就是\(\Theta(\sqrt nlog\sqrt n)\)
具体地,对于每一块,我们都存储排序前和排序后的序列,这样我们就解决了这个题。
对于size的大小,我解了一个方程,跑到了2052ms,bzoj17名,已经是这个解法(不使用平衡树)的极限了,还是非常满意。

顺便%%%强校XMYZ

Code

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
int n, q, m, block;
const int maxn = 1000001;
int a[maxn], b[maxn], pos[maxn], add[maxn];
using std::sort;
using std::min;
inline int read() {
  int x = 0, f = 1;
  char ch = getchar();
  while (!isdigit(ch)) {
    if (ch == '-')
      f = -1;
    ch = getchar();
  }
  while (isdigit(ch)) {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}
inline void reset(int x) {
  int l = (x - 1) * block + 1, r = min(x * block, n);
  for (int i = l; i <= r; i++)
    b[i] = a[i];
  sort(b + l, b + r + 1);
}
inline int find(int x, int v) {
  int l = (x - 1) * block + 1, r = min(x * block, n);
  int last = r;
  while (l <= r) {
    int mid = (l + r) >> 1;
    if (b[mid] < v)
      l = mid + 1;
    else
      r = mid - 1;
  }
  return last - l + 1;
}
inline void update(int x, int y, int v) {
  if (pos[x] == pos[y]) {
    for (int i = x; i <= y; i++)
      a[i] = a[i] + v;
  } else {
    for (int i = x; i <= pos[x] * block; i++)
      a[i] = a[i] + v;
    for (int i = (pos[y] - 1) * block + 1; i <= y; i++)
      a[i] = a[i] + v;
  }
  reset(pos[x]);
  reset(pos[y]);
  for (int i = pos[x] + 1; i < pos[y]; i++)
    add[i] += v;
}
inline int query(int x, int y, int v) {
  int sum = 0;
  if (pos[x] == pos[y]) {
    for (int i = x; i <= y; i++)
      if (a[i] + add[pos[i]] >= v)
        sum++;
  } else {
    for (int i = x; i <= pos[x] * block; i++)
      if (a[i] + add[pos[i]] >= v)
        sum++;
    for (int i = (pos[y] - 1) * block + 1; i <= y; i++)
      if (a[i] + add[pos[i]] >= v)
        sum++;
    for (int i = pos[x] + 1; i < pos[y]; i++)
      sum += find(i, v - add[i]);
  }
  return sum;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("input", "r", stdin);
#endif
  n = read(), q = read();
  if (n >= 500000)
    block = 3676;
  else if (n >= 5000) {
    block = 209;
  } else
    block = int(sqrt(n));
  for (int i = 1; i <= n; i++) {
    a[i] = read();
    pos[i] = (i - 1) / block + 1;
    b[i] = a[i];
  }
  if (n % block)
    m = n / block + 1;
  else
    m = n / block;
  for (int i = 1; i <= m; i++)
    reset(i);
  for (int i = 1; i <= q; i++) {
    char ch[5];
    int x, y, v;
    scanf("%s", ch);
    x = read(), y = read(), v = read();
    if (ch[0] == 'M')
      update(x, y, v);
    else
      printf("%d\n", query(x, y, v));
  }
}

posted on 2017-03-04 17:27 蒟蒻konjac 阅读(...) 评论(...) 编辑 收藏

统计