CF853E Lada Malina凸包,扫描线
Posted AThousandMoons
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF853E Lada Malina凸包,扫描线相关的知识,希望对你有一定的参考价值。
给定 \\(n\\) 个点 \\(\\mathbf{f_i}\\) 带权 \\(a_i\\) 和 \\(k\\) 个向量 \\(\\mathbf{v_i}\\),\\(q\\) 次询问 \\(\\mathbf p\\) 和 \\(t\\),求存在实数 \\(w_j\\in[-t,t]\\) 满足 \\(\\mathbf{f_i}+\\sum w_j\\mathbf{v_j}=\\mathbf p\\) 的 \\(a_i\\) 之和。
\\(2\\le k\\le 10\\),\\(1\\le n\\le 10^5\\),\\(|vx_i|,|vy_i|\\le 10^3\\),\\(|fx_i|,|fy_i|,|px|,|py|\\leq 10^9\\),\\(1\\leq a_i\\leq 10^9\\),\\(1\\leq t_i\\leq 10^5\\)。
后面的 \\(\\sum w_j\\mathbf{v_j}\\) 显然是一个凸包(Minkowski 和),但这个凸包咋求呢?
若 \\(vx_i<0\\land vx_i=0\\lor vy_i<0\\) 则令 \\(\\mathbf{v_i}\\) 取反,令 \\(\\mathbf p=\\mathbf p-t\\sum\\mathbf{v_j}\\),并移项得到 \\(\\mathbf{f_i}=\\mathbf p+\\sum w_j\\mathbf{v_j}\\),\\(w_j\\) 的取值范围变为 \\([0,2t]\\),此时直接把所有 \\(\\mathbf{v_j}\\) 按斜率排序,按从小到大的顺序从 \\(\\mathbf p\\) 开始加就是下凸壳,从大到小的顺序就是上凸壳。
因为凸包的边只有 \\(2k\\) 条,所以可以拆成一堆底边与 \\(y\\) 轴平行,直角边在 \\(-\\infty\\) 的直角梯形,约束即为 \\(x\\in[l,r]\\) 和 \\(y-kx\\leq b\\),令 \\(y:=y-kx\\) 之后就是二维数点,对纵坐标扫描线,对横坐标离散化之后用树状数组维护即可。
注意扫描线做相同纵坐标时的顺序(先询问还是先修改),时间复杂度 \\(O((n+q)k\\log n)\\)。
#include<bits/stdc++.h>
#define MP make_pair
#define PB emplace_back
#define all(x) x.begin(), x.end()
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100010;
int rd(){
int ch = getchar(), x = 0; bool f = false;
for(;ch < \'0\' || ch > \'9\';ch = getchar()) f |= ch == \'-\';
for(;ch >= \'0\' && ch <= \'9\';ch = getchar()) x = x * 10 + ch - \'0\';
return f ? -x : x;
}
int k, n, q, sx, sy, x[N], y[N], a[N], px[N], py[N], t[N];
pii v[11];
LL ans[N], tr[N];
vector<int> val;
int pos(int x){return upper_bound(all(val), x) - val.begin();}
struct Node {
double a;
int x, y, id;
Node(double _a = 0, int _x = 0, int _y = 0, int _z = 0): a(_a), x(_x), y(_y), id(_z){}
bool operator < (const Node &o) const {return fabs(a-o.a)<1e-7?id<o.id:a<o.a;}
} w[N<<1];
bool cmp(const pii &a, const pii &b){
if(!b.fi) return a.fi || a.se < b.se;
return a.se * b.fi < b.se * a.fi;
}
void upd(int p, int v){for(;p < N;p += p & -p) tr[p] += v;}
LL qry(int p){LL r = 0; for(;p;p -= p & -p) r += tr[p]; return r;}
int main(){
k = rd(); n = rd(); q = rd();
for(int i = 1;i <= k;++ i){
v[i].fi = rd(); v[i].se = rd();
if(v[i].fi < 0){
v[i].fi = -v[i].fi;
v[i].se = -v[i].se;
} else if(!v[i].fi && v[i].se < 0)
v[i].se = -v[i].se;
sx += v[i].fi; sy += v[i].se;
}
sort(v+1, v+k+1, cmp);
for(int i = 1;i <= n;++ i){
val.PB(x[i] = rd());
y[i] = rd(); a[i] = rd();
}
sort(all(val));
val.erase(unique(all(val)), val.end());
for(int i = 1;i <= q;++ i){
px[i] = rd(); py[i] = rd(); t[i] = rd();
px[i] -= sx*t[i]; py[i] -= sy*t[i]; t[i] <<= 1;
}
for(int i = 1;i <= k;++ i){
if(!v[i].fi){
for(int j = 1;j <= q;++ j)
py[j] += v[i].se * t[j];
continue;
}
double k = 1.*v[i].se/v[i].fi;
for(int j = 1;j <= n;++ j)
w[j] = Node(y[j] - k*x[j], x[j], a[j], 0);
for(int j = 1;j <= q;++ j){
w[n+j] = Node(py[j] - k*px[j], px[j], px[j] + v[i].fi*t[j], -j);
px[j] += v[i].fi*t[j]; py[j] += v[i].se*t[j];
}
sort(w+1, w+n+q+1); memset(tr, 0, sizeof tr);
for(int j = 1;j <= n+q;++ j)
if(w[j].id) ans[-w[j].id] -= qry(pos(w[j].y))-qry(pos(w[j].x-(i<2)));
else upd(pos(w[j].x), w[j].y);
}
for(int i = 1;i <= k;++ i){
if(!v[i].fi) break;
double k = 1.*v[i].se/v[i].fi;
for(int j = 1;j <= n;++ j)
w[j] = Node(y[j] - k*x[j], x[j], a[j], 0);
for(int j = 1;j <= q;++ j){
w[n+j] = Node(py[j] - k*px[j], px[j] - v[i].fi*t[j], px[j], j);
px[j] -= v[i].fi*t[j]; py[j] -= v[i].se*t[j];
}
sort(w+1, w+n+q+1); memset(tr, 0, sizeof tr);
for(int j = 1;j <= n+q;++ j)
if(w[j].id) ans[w[j].id] += qry(pos(w[j].y-(i>1))) - qry(pos(w[j].x-1));
else upd(pos(w[j].x), w[j].y);
}
for(int i = 1;i <= q;++ i) printf("%lld\\n", ans[i]);
}
以上是关于CF853E Lada Malina凸包,扫描线的主要内容,如果未能解决你的问题,请参考以下文章
CF-281C Rectangle Puzzle(凸包+面积)