1. 程式人生 > >[BZOJ4311]向量[線段樹分治+計算幾何+二分/三分]

[BZOJ4311]向量[線段樹分治+計算幾何+二分/三分]

一張手寫的題解

這些點一定在凸殼上

證明可以參照gxz大佬的題解

這個題的做法是按照詢問作為時間軸,把每個插入的向量視為在一個時間區間 \([l,r]\) 內有效,在 \([l,r]\) 線上段樹上對應的 \(O(log n)\) 個區間上打上標記,然後dfs一下整棵線段樹,對於每個dfs到的線段樹節點,遍歷這個節點有的標記,維護出凸殼,然後在凸殼上二分斜率(也可以三分~),更新這個節點對應區間的詢問的答案(這樣不需要進行刪除操作)

對著std調了好久...

每個節點被插入了 \(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;
}