[BZOJ4311]向量[线段树分治+计算几何+二分/三分]

一张手写的题解

这些点一定在凸壳上

证明可以参照gxz大佬的题解

这个题的做法是按照询问作为时间轴,把每个插入的向量视为在一个时间区间 \([l,r]\) 内有效,在 \([l,r]\) 在线段树上对应的 \(O(log n)\) 个区间上打上标记,然后dfs一下整棵线段树,对于每个dfs到的线段树节点,遍历这个节点有的标记,维护出凸壳,然后在凸壳上二分斜率(也可以三分~),更新这个节点对应区间的询问的答案(这样不需要进行删除操作)

对着std调了好久...

每次二分是 \(O(logn)\) 每个节点被插入了 \(O(logn)\) 次,总共插入了 \(O(nlogn)\)次,所以复杂度是 \(O(nlog^2n)\)

好像有个什么性质可以少个log,但这份代码居然跑进了前两页,所以似乎没必要了,其实是不想再写了

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 7;
typedef long long ll;
typedef long double lf;
const lf eps = 1e-8;
template<class A, class B> inline void chmax(A &a, B b) {
  if (a < b) a = b;
}
inline int read() {
  register int c = getchar(), x = 0;
  while (!isdigit(c)) c = getchar();
  while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
  return x;
}
struct Vec {
  int x, y;
  Vec(int x, int y) : x(x), y(y) {}
  Vec() {}
  inline Vec operator +(const Vec&b) const {return Vec(x + b.x, y + b.y);}
  inline Vec operator -(const Vec&b) const {return Vec(x - b.x, y - b.y);}
  inline ll operator *(const Vec&a) const {return x * 1ll * a.x + y * 1ll * a.y;}
  inline ll operator %(const Vec&a) const {return x * 1ll * a.y - y * 1ll * a.x;}
} q[MAXN];
struct atom {
  Vec a, b;//b是有效时间
} a[MAXN];
int n, op, x, y, sta[MAXN], top, tot, tot1; //因为栈存的是编号,所以数组嵌套有点多,可以存vec减少嵌套
ll ans[MAXN];
vector<int> t[530000];
inline bool cmp(int x, int y) {
  if (a[x].a.x != a[y].a.x) return a[x].a.x < a[y].a.x;//按照x,y排序 求出凸壳
  return a[x].a.y < a[y].a.y;
}
inline void add(int o, int l, int r, int ql, int qr, int v) {
  if (ql > qr) return ;
  if (ql <= l && r <= qr) return void(t[o].push_back(v));
  int mid = l + r >> 1;
  if (ql <= mid) add(o << 1, l, mid, ql, qr, v);
  if (qr > mid) add(o << 1 | 1, mid + 1, r, ql, qr, v);
}
inline lf slope(const Vec &a, const Vec &b) {
  if (fabs(a.x - b.x) < eps) return 1e9;
  return lf(a.y - b.y) / (a.x - b.x);
}
inline ll query(int o, int pos) { //二分q[pos]垂线的斜率处于凸包的哪个位置
  lf K = -1.0 * q[pos].x / q[pos].y;
  //-1.0 / (q[pos].y / q[pos].x);
  if (top == 1 || K > slope(a[t[o][sta[1]]].a, a[t[o][sta[2]]].a) + eps) return q[pos] * a[t[o][sta[1]]].a;
  if (K + eps < slope(a[t[o][sta[top - 1]]].a, a[t[o][sta[top]]].a)) return q[pos] * a[t[o][sta[top]]].a;//判边界,不知道哪些不需要...但判了总没坏处
  int l = 2, r = top, ans = 1;
  while (l <= r) {
    int mid = l + r >> 1;
    if (K < slope(a[t[o][sta[mid - 1]]].a, a[t[o][sta[mid]]].a) + eps) ans = mid, l = mid + 1;
    else r = mid - 1;
  }
  return q[pos] * a[t[o][sta[ans]]].a;
}
 
inline void solve(int o, int l, int r) {
  if (!t[o].size()) return ;
  sort(t[o].begin(), t[o].end(), cmp);
  top = 0;
  for (int i = 0; i < t[o].size(); ++i) {
    while (top > 1 && (a[t[o][i]].a - a[t[o][sta[top - 1]]].a) % (a[t[o][sta[top]]].a - a[t[o][sta[top - 1]]].a) <= 0) --top;//维护凸壳
    sta[++top] = i;
  }
  for (int i = l; i <= r; ++i) chmax(ans[i], query(o, i));
}
 
inline void dfs(int o, int l, int r) {
  solve(o, l, r);
  if (l == r) return ;
  int mid = l + r >> 1;
  dfs(o << 1, l, mid), dfs(o << 1 | 1, mid + 1, r);
}
 
int main() {
  int m = read();
  while (m--) {
    op = read(), x = read();
    if (op != 2) {
      y = read();
      if (op == 1) a[++tot1] = (atom) {Vec(x, y), Vec(tot + 1, -1)};
      else q[++tot] = Vec(x, y);
    }
    else a[x].b.y = tot;
  }
  for (int i = 1; i <= tot1; ++i) add(1, 1, tot, a[i].b.x, a[i].b.y == -1 ? tot : a[i].b.y, i);
  dfs(1, 1, tot);
  for (int i = 1; i <= tot; ++i) printf("%lld\n", ans[i]);
  return 0;
}
posted @ 2018-12-28 19:29  QvvQ  阅读(244)  评论(0编辑  收藏  举报