【YBT2023寒假Day15 B】堵命运枪(计算几何)(Pick定理)

堵命运枪

题目链接:YBT2023寒假Day15 B

题目大意

给你一个凸多边形,保证没有三点贡献,随机一个至少包含三个点的点集,问你这些点形成的新凸多边形中,严格在这个凸多边形中的点数的期望。

思路

首先我们考虑给你一个多边形要怎么算点数。
有个叫 Pick 定理的东西,简单格点多边形的面积等于边上的点数 / 2 + /2+ /2+内部点数 − 1 -1 1
那内部点数就是:多边形面积 + 1 − +1- +1边上的点数 / 2 /2 /2

多边形的面积我们就直接用叉积除二那个求。
边上的点数也不难因为给你的点都在整点上,就直接是横纵坐标差的 gcd ⁡ \gcd gcd 就对了。


接下来考虑子集。
首先不如算每个情况的答案和,再除以总数 2 n − 1 − n − ( n 2 ) 2^n-1-n-\binom{n}{2} 2n1n(2n)
那我们考虑对于每条边统计贡献,因为你注意到每个边贡献的方式都只跟自己有关。

那考虑要怎样才能贡献,除了选它这两个点。
那就是至少还要选一个点,而且不能两侧都选。
那我们为了便于统计,我们只选左边的,那如果左侧 a a a 个点,右侧 b b b 个点(右侧包括这两个点)。
概率是 ( 1 − 2 − a ) 2 − b (1-2^{-a})2^{-b} (12a)2b

那你每个贡献都乘上概率就是了。


但是还有一个问题就是你枚举点对是 O ( n 2 ) O(n^2) O(n2) 的。
不过注意到它这里有精度,而且你右侧点越多,概率越小,那我们到很小很小的时候,概率就忽略不计了,那我们只用枚举大概 60 60 60 个旁边的点就差不多了。

代码

#include<cstdio>

using namespace std;

const int N = 1e5 + 100;	
struct node {
	double x, y;
}a[N];
int n;
double two[N], sum, outsum, line;

node operator +(node x, node y) {return (node){x.x + y.x, x.y + y.y};}
node operator -(node x, node y) {return (node){x.x - y.x, x.y - y.y};}
double operator *(node x, node y) {return x.x * y.x + x.y * y.y;}
double operator ^(node x, node y) {return x.x * y.y - x.y * y.x;}
int abs(int x) {return x < 0 ? -x : x;}
int gcd(int x, int y) {
	if (!y) return x;
	return gcd(y, x % y);
}

double P(int a) {//右侧(包含端点) 
	return (two[a] - two[n]) / (1.0 - two[n] * (1.0 + 1.0 * n + 1.0 * n * (n - 1) / 2.0));
}

int main() {
	freopen("ak.in", "r", stdin);
	freopen("ak.out", "w", stdout);
	
	scanf("%d", &n);
	for (int i = 0; i < n; i++) scanf("%lf %lf", &a[i].x, &a[i].y);
	
	two[0] = 1; for (int i = 1; i < N; i++) two[i] = two[i - 1] / 2.0;
	for (int i = 2; i < n; i++) sum += ((a[i - 1] - a[0]) ^ (a[i] - a[0])) / 2.0;
	 
	for (int l = 0; l < n; l++) {
		double now = 0;
		for (int sz = 1; sz < n && sz <= 60; sz++) {
			int r = (l + sz) % n;
			now += ((a[(r - 1 + n) % n] - a[l]) ^ (a[r] - a[l])) / 2.0;//统计在新多边形外面的面积
			outsum += P(sz + 1) * now; line += P(sz + 1) * gcd(abs(a[l].x - a[r].x), abs(a[l].y - a[r].y)) / 2.0;
		}
	}
	
	printf("%.12lf", sum - outsum - line + 1.0);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值