zoukankan      html  css  js  c++  java
  • JSOI2018 战争(凸包+闵可夫斯基和)

    JSOI2018 战争

    题解

    • 每次询问暴力判断时间无法满足,考虑是否可以求出使两凸包有交或无交的向量所在的范围。
    • 若两个凸包平移向量 w w w后有交,等价于 ∃ a ∈ A , b ∈ B , a = b + w exists ain A,bin B,a=b+w aA,bB,a=b+w,即 ∃ a ∈ A , b ∈ B , w = a − b exists ain A,bin B, w=a-b aA,bB,w=ab,则所有 w w w的取值范围是 { a − b ∣ a ∈ A , b ∈ B } {a-b|ain A,bin B} {abaA,bB},把 b b b向量全部取反,和 a a a做一遍闵可夫斯基和得到的凸包就是能使两凸包有交的向量范围(包括凸包的边)。
    • 最后只需要判断向量是否在凸包上,如果用回转数或光线投射复杂度是 O ( n q ) O(nq) O(nq)的,可以先把凸包最左下角的点平移到 ( 1 , 1 ) (1,1) (1,1),二分出该向量的极角序在哪两个顶点之间,然后判断该向量是否在这两个向量和 ( 1 , 1 ) (1,1) (1,1)构成的三角形内,复杂度降为 O ( q log ⁡ 2 n ) O(qlog_2 n) O(qlog2n)

    代码

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define N 200010
    #define ld long double
    #define ll long long
    struct no {
    	ll x, y, s;
    	ld d;
    }a[N], b[N], a0[N], b0[N], q[N];
    int n, m, t;
    ll sqr(ll x) {
    	return x * x;
    }
    int cmp(no x, no y) {
    	if(x.y == y.y) return x.x < y.x;
    	return x.y < y.y;
    }
    int cmp1(no x, no y) {
    	if(x.d == y.d) return x.s > y.s;
    	return x.d < y.d;
    }
    no in(no x, no y) {
    	return {x.x + y.x, x.y + y.y};
    }
    no de(no x, no y) {
    	return {x.x - y.x, x.y - y.y};
    }
    ll ct(no x, no y) {
    	return x.x * y.y - x.y * y.x;
    }
    void solve() {
    	int i;
    	sort(a + 1, a + n + 1, cmp);
    	for(i = 2; i <= n; i++) {
    		a[i].d = atan2(a[i].y - a[1].y, a[i].x - a[1].x);
    		a[i].s = sqr(a[i].y - a[1].y) + sqr(a[i].x - a[1].x);
    	}
    	sort(a + 2, a + n + 1, cmp1);
    	t = 1;
    	q[1] = a[1];
    	for(i = 2; i <= n; i++) if(i == 2 || a[i].d != a[i - 1].d) {
    		while(t > 1 && ct(de(q[t], q[t - 1]), de(a[i], q[t])) < 0) t--;
    		q[++t] = a[i];
    	}
    }
    int main() {
    	int Q, i;
    	scanf("%d%d%d", &n, &m, &Q);
    	for(i = 1; i <= n; i++) {
    		scanf("%lld%lld", &a[i].x, &a[i].y);
    	}
    	for(i = 1; i <= m; i++) {
    		scanf("%lld%lld", &b[i].x, &b[i].y);
    		b[i].x = -b[i].x, b[i].y = -b[i].y;
    	}
    	solve();
    	int at = t;
    	for(i = 1; i <= t; i++) a0[i] = q[i];
    	n = m;
    	for(i = 1; i <= m; i++) a[i] = b[i];
    	solve();
    	int bt = t; 
    	for(i = 1; i <= t; i++) b0[i] = q[i];
    	int tot = 0;
    	for(i = 1; i <= at; i++) {
    		a[++tot] = de(a0[i % at + 1], a0[i]);
    		a[tot].d = atan2(a[tot].y, a[tot].x);
    		if(a[tot].d < 0) a[tot].d += M_PI * 2.0;
    	}
    	for(i = 1; i <= bt; i++) {
    		a[++tot] = de(b0[i % bt + 1], b0[i]);
    		a[tot].d = atan2(a[tot].y, a[tot].x);
    		if(a[tot].d < 0) a[tot].d += M_PI * 2.0;
    	}
    	sort(a + 1, a + tot + 1, cmp1);
    	q[1] = in(a0[1], b0[1]);
    	for(i = 1; i < tot; i++) q[i + 1] = in(q[i], a[i]);
    	for(i = 2; i <= tot; i++) {
    		q[i].d = atan2(q[i].y - q[1].y, q[i].x - q[1].x);
    	}
    	while(Q--) {
    		no x;
    		scanf("%lld%lld", &x.x, &x.y);
    		x.d = atan2(x.y - q[1].y, x.x - q[1].x);
    		if(x.d > q[tot].d || x.d < 0) {
    			printf("0
    ");
    			continue;
    		}
    		int l = 1, r = tot - 1, s;
    		while(l <= r) {
    			int mid = (l + r) / 2;
    			if(x.d >= q[mid].d) s = mid, l = mid + 1; else r = mid - 1;
    		}
    		if(ct(de(x, q[s]), de(q[s + 1], x)) <= 0) printf("1
    "); else printf("0
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    我的 vs code 中setting 设置
    创建Vue cli 脚手架中遇到的空格,函数问题的解决
    C#与.Net的关系
    c#可以开发哪些类型的应用程序
    json注解使用(jackson与fastjson)
    MySQL优化:如何避免回表查询
    二叉树基础知识总结
    Redis,Memcache,MongoDb的特点
    MySQL的InnoDB存储引擎为什么要用自增的主键?
    分布式事务atomikos使用
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279505.html
Copyright © 2011-2022 走看看