HDU-7112 计算几何

题目大意:

已知一个凸包的前提下,每次查询给出一个三角形,求平面内可以和三角形内的点构成射线穿过凸包但构成线段不穿过凸包的点的面积。

解题思路:

(因为官方思路更加清晰,所以这里借用官方的思路)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e5 + 10;
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
struct Point {
    ll x, y;
    Point operator - (Point p) {return {x - p.x, y - p.y};}
	bool operator == (Point p) {return ((x == p.x) && (y == p.y));}
    ll operator ^ (Point p) {return x * p.y - y * p.x;}
    ll dis(Point p) {return hypot(x - p.x, y - p.y);}
} tri[4];
Point id;
bool cmp(Point a, Point b) {
	double x = (a - id) ^ (b - id);
	if (x > 0) return true;
	if (x == 0 && a.dis(id) < b.dis(id)) return true;
	return false;
}
ll multi(Point p1, Point p2, Point p3) {return (p2 - p1) ^ (p3 - p2);}
struct Pol {
    int n;
    Point p[maxn]; //凸包是逆时针旋转
    ll s[maxn];
    void init() {
        for (int i = 3; i <= n; i++)
            s[i] = abs((p[i - 1] - p[1]) ^ (p[i] - p[1])) + s[i - 1]; //算面积
    }
	void convex() { //构建凸包
		int k = 1;
		for (int i = 2; i <= n; i++)
			if (p[i].x < p[k].x || p[i].x == p[k].x && p[i].y < p[k].y) //找最左边的,以及最下面的都行
				k = i;
		swap(p[1], p[k]);
		id = p[1];
		sort (p + 2, p + n + 1, cmp);
		int t = 1;
		for (int i = 2; i <= n; i++) {
			while (t > 1 && multi(p[t - 1], p[t], p[i]) <= 0) t--;
			p[++t] = p[i];
		}
		n = t;
	}
    template <typename F>
    int find(F f) { //凸包从1开始
        bool up = f(1, 2); 
        int l = 1, r = n + 1;
        while (l != r) {
            int m = (l + r) / 2;
            if(f(1, m)){
                if (up) l = m + 1;
                else r = m;
            } else {
                if (f(m, m + 1)) r = m;
                else l = m + 1;
            }
        }
        return l;
    }
    pii findtangent(Point q) { //求凸包外一点的两条切线(逆十字板子)
        int l=find([&](int i,int j){return ((p[i] - q) ^ (p[j] - q)) < 0;}),
        r=find([&](int i,int j){return ((p[i] - q) ^ (p[j] - q)) > 0;});
        return {l, r};
    }
} pol, pol_tri;
pii seg[4];
int vis[20];
ll solve() {
	memset(vis, 0, sizeof(vis));
	ll ans = 0;
	pol_tri.convex();
	pol_tri.p[pol_tri.n + 1] = pol_tri.p[1];
	pol_tri.init();
	ans = pol_tri.s[pol_tri.n]; //初始答案,即图中的粉色区域
	int op;
	pol_tri.p[0] = pol_tri.p[pol_tri.n];
	for (int i = 1; i <= pol_tri.n; i++) { //找到公切线上经过旧凸包上的点
		for (int j = 1; j <= 3; j++) {
			if (pol_tri.p[i] == pol.p[seg[j].ft])
				vis[i] = seg[j].ft;
			else if (pol_tri.p[i] == pol.p[seg[j].sd])
				vis[i] = seg[j].sd;
		}
	}
	for (int i = 1; i <= pol_tri.n; i++) if (vis[i] > pol.n) vis[i] -= pol.n; //因为可能有n+1,所以减去n
	vis[0] = vis[pol_tri.n]; 
	for (int i = 1; i <= pol_tri.n; i++) {
		if (vis[i] && !vis[i - 1]) { //新凸包也是逆时针的,找到第一个在旧凸包上的点
			op = i;
			break;
		}
	}
	if (!vis[op % pol_tri.n + 1]) return ans - pol.s[pol.n]; //如果只有一个切点,则说明旧凸包包含在新凸包内
	ll s = 0;
	//计算图中的紫色区域 或 旧凸包减去紫色区域的面积
	//ans-s代表s恰好是紫色区域
	//ans - (pol.s[pol.n] - s) (pol.s[pol.n] - s)才为紫色区域	
	s = abs((pol_tri.p[op] - pol.p[1]) ^ (pol_tri.p[op % pol_tri.n + 1] - pol.p[1])); 
	s += pol.s[min(vis[op], vis[op % pol_tri.n + 1])] + (pol.s[pol.n] - pol.s[max(vis[op], vis[op % pol_tri.n + 1])]);
	
	//通过判断旧凸包开始的点来得出s是哪块区域
	ll sgn1 = (pol.p[vis[op % pol_tri.n + 1]] - pol.p[vis[op]]) ^ (pol_tri.p[op - 1] - pol_tri.p[op]);
	ll sgn2 = (pol.p[vis[op % pol_tri.n + 1]] - pol.p[vis[op]]) ^ (pol.p[1] - pol_tri.p[op]);
	//分类讨论
	if (vis[op] == 1) return ans - s; //切点恰好就是旧凸包开始的那个点
	else if (vis[op % pol_tri.n + 1] == 1) return ans - (pol.s[pol.n] - s);
	else {
		if (sgn1 > 0 && sgn2 > 0) return ans - s;
		else return ans - (pol.s[pol.n] - s);
	}
}
int main() {
	IOS; //开IOS,不然cin,cout会tle
    int _, q;
    cin >> _;
    while (_--) {
        cin >> pol.n;
        for (int i = 1; i <= pol.n; i++)
            cin >> pol.p[i].x >> pol.p[i].y; //逆时针输入
        pol.p[pol.n + 1] = pol.p[1];
        pol.init();
        cin >> q;
        while (q--) {
            for (int i = 1; i <= 3; i++)
                cin >> tri[i].x >> tri[i].y;
            for (int i = 1; i <= 3; i++)
                seg[i] = pol.findtangent(tri[i]);
			pol_tri.n = 0;
            for (int i = 1; i <= 3; i++) { //将六个切点和三角形上的点构建一个新凸包,新凸包上的和旧凸包上相同的点就是三角形和旧凸包公切线经过的点
				pol_tri.p[++pol_tri.n] = tri[i];
				pol_tri.p[++pol_tri.n] = pol.p[seg[i].ft];
				pol_tri.p[++pol_tri.n] = pol.p[seg[i].sd];
			}
			cout << solve() << endl;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值