SCP-S模拟56 C题

应动动要求,回cnblogs发篇博客。

首先从左往右枚举矩形的右边界,再从右往左枚举该右边界的左边界,[L, R]。

那么矩形们的面积就是$(R - L) * \sum h$。问题在于如何快速算出$\sum h$。

$h = y_{max} - y_{min}$。考虑从下往上枚举$y_{max}$,在左端点那条直线上存在最大的$y_{qaq} \le y_{max}$,而之前扫过的直线中的所有$y_i \in (y_{qaq}, y_{max})$都是有贡献的。小于第一个小于$y_{qaq}$的高度$y_{min}$的点都可以与这些$y_i$产生符合要求的矩形边长。设这种点有s个,只考虑一个y,它产生的边界的和就是$s * y - \sum \limits_{y_i \le y_{min}} y_i$。

把所有符合条件的y产生的边长累加起来,最后每个左边界产生的面积为:$\sum(R - L) * (s_{y_i \le y_{min}的数量} * \sum \limits_{y_i \in (y_{qaq}, y_{max})} - \sum \limits_{y_i \le y_{min}} y_i * s_{y_i \in (y_{qaq}, y_{max})})$。$y_{max}$是每个枚举的左边界从下向上枚举得到的。

长度和和数量和都可以用树状数组快速求出。
 
我知道动动在等我的代码时刻
 
#include <bits/stdc++.h>

const int D = 1e9 + 7;
int n, c[2510], pos[2510][2510];
bool used[2510];

struct Bit {
  int t[2510];
  void add(int x, int d) {
    for (; x <= 2510; x += x & -x) t[x] += d;
  }
  int ask(int x) {
    int ret = 0;
    for (; x; x -= x & -x) ret += t[x];
    return ret;
  }
  void clear() {
    for (int i = 1; i <= 2510; ++i) t[i] = 0;
  }
} siz, len;

inline int read() {
  int a = 0, b = 0;
  char c = getchar();
  while (!isdigit(c)) b = c == '-' ? 1 : 0, c = getchar();
  while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
  return b ? -a : a;
}

void modify(int x) {
  if (!used[x]) {
    used[x] = true;
    siz.add(x, 1), len.add(x, x);
  }
}

signed main() {
  n = read();
  for (int i = 1, x, y; i <= n; ++i) x = read(), y = read(), pos[x][++c[x]] = y;
  for (int i = 1; i <= 2500; ++i)
    std::sort(pos[i] + 1, pos[i] + 1 + c[i]), pos[i][c[i] + 1] = 2501;
  long long ans = 0;
  for (int i = 1; i <= 2500; ++i) {
    if (!c[i]) continue;
    siz.clear(), len.clear();
    for (int j = 1; j    2500; ++j) used[j] = false;
    for (int j = 1; j <= c[i]; ++j) modify(pos[i][j]);
    for (int j = i - 1; j; --j) {
      if (!c[j]) continue;
      int p1 = 1, p2 = 1, cur = std::max(pos[i][1], pos[j][1]);
      for (int k = 1; k <= c[j]; ++k) modify(pos[j][k]);
      while (pos[i][p1 + 1] <= cur) ++p1;
      while (pos[j][p2 + 1] <= cur) ++p2;
      while (p1 <= c[i] && p2 <= c[j]) {
        int mn = std::min(pos[i][p1], pos[j][p2]);
        int trend = std::min(pos[i][p1 + 1], pos[j][p2 + 1]);
        (ans += (long long) (i - j) *
                ((long long) (len.ask(trend - 1) - len.ask(cur - 1)) * siz.ask(mn) -
                 (long long) (siz.ask(trend - 1) - siz.ask(cur - 1)) * len.ask(mn))) %= D;
        cur = trend;
        if (pos[i][p1 + 1] <= cur) ++p1;
        if (pos[j][p2 + 1] <= cur) ++p2;
      }
    }
  }
  return !printf("%lld\n", ans);
}
颓代码不可取
posted @ 2019-10-02 15:43  Gekoo  阅读(464)  评论(3编辑  收藏  举报