【NOI2019模拟2019.6.14】最大面积(闵科夫斯基和)

Description:

在这里插入图片描述

1<=n<=1e5,1<=q<=1e6

题解:

JSOI2018-战争这道题是闵科夫斯基和裸题。

闵科夫斯基和的作用是在凸包上有奇效,即凸包A和凸包B的闵科夫斯基和的凸包的大小是 ∣ A ∣ + ∣ B ∣ |A|+|B| A+B

先说这个怎么做:
找到两个凸包最上最左的点,加起来后的新点显然是凸包,接下来维护两个指针,一开始在开头,接下来谁的向量的几角大就加谁(也可以直接用叉积判),扫完后就是新凸包。

再考虑这个破题,不如利用分治,对于区间[l…r],mid=(l+r)/2,

对于[l…mid]的每个后缀,搞出凸包,[mid+1…r]的每一个前缀,也搞出凸包,然后就闵科夫斯基和合并就能得到新的凸包。

凸包的点数和是 O ( n   l o g   n ) O(n~log~n) O(n log n),最后对大的再做个凸包。

查询是询问凸包上的点和O形成的向量×询问向量的最大值。

三角形面积最大,因为底确定,相当于高最大,也就是凸包上相邻两点的向量于查询向量最平行。

这个二分一下就好了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define ul unsigned long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;

int n, Q;
struct P {
	ll x, y;
	P(ll _x = 0, ll _y = 0) { x = _x, y = _y;}
} a[N], w;

P operator + (P a, P b) { return P(a.x + b.x, a.y + b.y);}
P operator - (P a, P b) { return P(a.x - b.x, a.y - b.y);}
ll operator ^ (P a, P b) { return a.x * b.y - a.y * b.x;}
bool operator < (P a, P b) { return a.y == b.y ? a.x < b.x : a.y > b.y;}

P _d[N * 20];
int us[N], z[N], z0, zm;
void build_tb(P *a, int &a0) {
	sort(a + 1, a + a0 + 1);
	fo(i, 1, a0) us[i] = 0;
	z[z0 = 1] = 1;
	fo(i, 2, a0) {
		while(z0 > 1 && ((a[z[z0]] - a[z[z0 - 1]]) ^ (a[i] - a[z[z0]])) <= 0)
			us[z[z0 --]] = 0;
		us[z[++ z0] = i] = 1;
	}
	zm = z0;
	fd(i, a0 - 1, 1) if(!us[i]) {
		while(z0 > zm && ((a[z[z0]] - a[z[z0 - 1]]) ^ (a[i] - a[z[z0]])) <= 0)
			us[z[z0 --]] = 0;
		us[z[++ z0] = i] = 1;
	}
	z0 --;
	fo(i, 1, a0) _d[i] = a[i];
	a0 = 0;
	fo(i, 1, z0) a[++ a0] = _d[z[i]];
}


P d[N * 20]; int d0;
P p[N], q[N]; int p0, q0;

void dg(int x, int y) {
	if(x == y) { d[++ d0] = a[x]; return;}
	int m = x + y >> 1;
	dg(x, m); dg(m + 1, y);
	p[p0 = 1] = a[m];
	fd(i, m - 1, x) p0 ++, p[p0] = p[p0 - 1] + a[i];
	q[q0 = 1] = a[m + 1];
	fo(i, m + 2, y) q0 ++, q[q0] = q[q0 - 1] + a[i];
	build_tb(p, p0); build_tb(q, q0);
	p[++ p0] = p[1]; q[++ q0] = q[1];
	int l = 1, r = 1; P u = p[1] + q[1], v = u;
	d[++ d0] = u;
	while(l < p0 && r < q0) {
		if(((p[l + 1] - p[l]) ^ (q[r + 1] - q[r])) >= 0) {
			v = u + (p[l + 1] - p[l]);
			d[++ d0] = v;
			l ++; u = v;
		} else {
			v = u + (q[r + 1] - q[r]);
			d[++ d0] = v;
			r ++; u = v;
		}
	}
	while(l < p0) {
		v = u + (p[l + 1] - p[l]);
		d[++ d0] = v;
		l ++; u = v;
	}
	while(r < q0) {
		v = u + (q[r + 1] - q[r]);
		d[++ d0] = v;
		r ++; u = v;
	}
}

#define db double
int cmp2(P a, P b) {
	return atan2(a.y, a.x) < atan2(b.y, b.x);
}

db jj(P a) { return atan2(a.y, a.x);}
db jo[N * 2];

int main() {
	freopen("area.in", "r", stdin);
	freopen("area.out", "w", stdout);
	scanf("%d %d", &n, &Q);
	fo(i, 1, n) scanf("%lld %lld", &a[i].x, &a[i].y), a[i].y = a[i].y;
	dg(1, n);
	build_tb(d, d0);
	fo(i, 1, d0 - 1) jo[i] = jj(d[i + 1] - d[i]);
	jo[d0] = jj(d[1] - d[d0]);
	fo(ii, 1, Q) {
		scanf("%lld %lld", &w.x, &w.y);
		db wj = atan2(-w.y, -w.x);
		ll ans = max(w ^ d[1], w ^ d[d0]);
		int as = 0;
		for(int l = 1, r = d0; l <= r; ) {
			int m = l + r >> 1;
			if(jo[m] <= wj) as = m, l = m + 1; else r = m - 1;
		}
		if(as) {
			ans = max(ans, w ^ d[as]);
			ans = max(ans, w ^ d[as + 1]);
		}
		pp("%lld\n", ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值