CF559D Randomizer 题解

Codeforces
Luogu

Description.

给定一个凸包,随机选一个点数 \(\ge3\) 的点集。
问选出点集构成凸包内整点数的期望。
精度 \(10^{-9}\)

Solution.

首先,凸包内整点显然想到皮克定理。
皮克定理是 \(Area=Cnt_{inside}+\frac{Cnt_{edge}}{2}-1\)
然后如果求凸包内好像牵扯的信息比较多,可以考虑容斥。
答案就是原凸包内的整点(算边)减去被切掉的凸包内整点(算边)。
然后被切掉凸包肯定是原凸包上的一段序列。
于是显然就有一个 \(O(n^2)\) 做法,就考虑枚举新凸包的下一个点。
可以直接维护,概率是 \(\frac{2^{n-k}-1}{2^n-1-n-n\times(n-1)\div 2}\),其中 \(k\) 表示选点个数。

但是怎么从 \(O(n^2)\) 优化到更优呢。
哈哈,我们发现 \(\frac{1}{60}\le 10^{18}\),所以直接枚举后 \(60\) 个点即可,复杂度变成了 \(O\left(n\log\left(\frac{10^9}{10^{-9}}\right)\right)\),直接 A 了!

Coding.

点击查看菜瓜代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
const int N=100005;int n;typedef long double db;
struct vec
{
	int x,y;
	inline vec operator+(vec b) {return(vec){x+b.x,y+b.y};}
	inline vec operator-(vec b) {return(vec){x-b.x,y-b.y};}
	inline ll operator*(vec b) {return 1ll*x*b.y-1ll*y*b.x;}
	inline int count() {return abs(__gcd(x,y));}
}a[N];db pw[N];
inline db gl(int k)
{
	if(n>100) return 1.0/pw[k];
	return (pw[n-k]-1)/(pw[n]-1-n-1.0*n*(n-1)/2);
}
int main()
{
	read(n);for(int i=1;i<=n;i++) read(a[i].x,a[i].y);
	pw[0]=1;for(int i=1;i<=100;i++) pw[i]=pw[i-1]*2;
	ll Cn=0,S=0;for(int i=1;i<=n;i++) S+=a[i]*a[i%n+1],Cn+=(a[i%n+1]-a[i]).count();
	db del=0;for(int i=1;i<=n;i++)
	{
		ll s=a[i]*a[i%n+1],cn=(a[i%n+1]-a[i]).count();
		for(int k=3,j=(i+1)%n+1;k<=60&&j%n+1!=i;k++,j=j%n+1)
		{
			s+=a[(j-2+n)%n+1]*a[j],cn+=(a[j]-a[(j-2+n)%n+1]).count();
			del+=gl(k)*(s+a[j]*a[i]-cn+(a[i]-a[j]).count())/2.0;
#ifdef debug
			printf("gl : %.10Lf\n",gl(k));
			printf("del : %.10lf\n",(s+a[j]*a[i]-cn+(a[i]-a[j]).count())/2.0);
#endif
		}
	}
	return printf("%.10Lf\n",(S+2-Cn)/2.0-del),0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15191383.html