1. 程式人生 > >【BZOJ】4311: 向量(線段樹分治板子題)

【BZOJ】4311: 向量(線段樹分治板子題)

char 給定 dot 區間 int() str friend 所有 bre

題解

我們可以根據點積的定義,垂直於原點到給定點構成的直線作一條直線,從正無窮往下平移,第一個碰到的點就是答案

像什麽,上凸殼哇

可是……動態維護上凸殼?

我們可以離線,計算每個點能造成貢獻的一個詢問區間[l,r]表示這個點在第l個詢問和第r個詢問之間存在,按照每個點的橫坐標大小順序插入線段樹,我們就可以類似斜率優化構造出凸包

對於所有詢問,我們可以給它們按極角排序,然後遍歷線段樹,如果按照極角排序,那麽垂直於他們的直線斜率遞減,最優點也右移

實現的方法就是一邊遍歷線段樹,一邊歸並排序,每一層按照極角序遍歷這個凸殼就好

代碼

#include <iostream>
#include <cstdio>
#include <vector> #include <algorithm> #include <cmath> #include <cstring> #include <map> //#define ivorysi #define pb push_back #define space putchar(‘ ‘) #define enter putchar(‘\n‘) #define mp make_pair #define pb push_back #define fi first #define se second #define mo 974711
#define MAXN 200005 #define RG register using namespace std; typedef long long int64; typedef double db; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); } while
(c >= ‘0‘ && c <= ‘9‘) { res = res * 10 + c - ‘0‘; c = getchar(); } res *= f; } template<class T> void out(T x) { if(x < 0) {putchar(‘-‘);x = -x;} if(x >= 10) { out(x / 10); } putchar(‘0‘ + x % 10); } struct Point { int64 x,y; Point(){} Point(int64 _x,int64 _y) { x = _x;y = _y; } friend Point operator + (const Point &a,const Point &b) { return Point(a.x + b.x,a.y + b.y); } friend Point operator - (const Point &a,const Point &b) { return Point(a.x - b.x,a.y - b.y); } friend int64 operator * (const Point &a,const Point &b) { return a.x * b.y - a.y * b.x; } friend int64 dot(const Point &a,const Point &b) { return a.x * b.x + a.y * b.y; } friend bool operator < (const Point &a,const Point &b) { return a.x < b.x; } }; struct Inode { Point a;int l,r; friend bool operator < (const Inode &s,const Inode &t) { return s.a < t.a; } }Ins[MAXN]; struct Qnode { Point a;int id; }Qry[MAXN],tmp[MAXN]; vector<Point> tr[MAXN * 4]; int N,cntI,cntQ,st[MAXN * 4]; int64 ans[MAXN]; void Insert(int u,int L,int R,int l,int r,Point a) { if(L == l && R == r) { int s = tr[u].size() - 1; while(s > 0) { if((tr[u][s] - tr[u][s - 1]) * (a - tr[u][s - 1]) >= 0) { tr[u].pop_back(); } else break; --s; } tr[u].pb(a); return ; } int mid = (L + R) >> 1; if(r <= mid) Insert(u << 1,L,mid,l,r,a); else if(l > mid) Insert(u << 1 | 1,mid + 1,R,l,r,a); else { Insert(u << 1,L,mid,l,mid,a); Insert(u << 1 | 1,mid + 1,R,mid + 1,r,a); } } void Init() { read(N); int t,id;int64 x,y; for(int i = 1 ; i <= N ; ++i) { read(t); if(t == 1) { read(x);read(y); Ins[++cntI] = (Inode){Point(x,y),cntQ + 1,-1}; } else if(t == 3) { read(x);read(y);++cntQ; Qry[cntQ] = (Qnode){Point(x,y),cntQ}; } else { read(id); Ins[id].r = cntQ; } } for(int i = 1 ; i <= cntI ; ++i) { if(Ins[i].r == -1) Ins[i].r = cntQ; } sort(Ins + 1,Ins + cntI + 1); for(int i = 1 ; i <= cntI ; ++i) { if(Ins[i].l <= Ins[i].r) { Insert(1,1,cntQ,Ins[i].l,Ins[i].r,Ins[i].a); } } } void Solve(int u,int L,int R) { int mid = (L + R) >> 1; if(L != R) { Solve(u << 1,L,mid);Solve(u << 1 | 1,mid + 1,R); int tl = L,tr = mid + 1,p = L; while(tl <= mid && tr <= R) { if(Qry[tr].a * Qry[tl].a >= 0) tmp[p++] = Qry[tl++]; else tmp[p++] = Qry[tr++]; } while(tl <= mid) tmp[p++] = Qry[tl++]; while(tr <= R) tmp[p++] = Qry[tr++]; for(int i = L ; i <= R ; ++i) Qry[i] = tmp[i]; } int s = tr[u].size() - 1; if(s != -1) { for(int i = L ; i <= R ; ++i) { while(st[u] < s) { if(dot(tr[u][st[u]],Qry[i].a) <= dot(tr[u][st[u] + 1],Qry[i].a)) { ++st[u]; } else break; } ans[Qry[i].id] = max(ans[Qry[i].id],dot(Qry[i].a,tr[u][st[u]])); } } } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif Init(); Solve(1,1,cntQ); for(int i = 1 ; i <= cntQ ; ++i) out(ans[i]),enter; return 0; }

【BZOJ】4311: 向量(線段樹分治板子題)