Gym 101873G - Water Testing - [皮克定理]
题意:
在点阵上,给出 N 个点的坐标(全部都是在格点上),将它们按顺序连接可以构成一个多边形,求该多边形内包含的格点的数目。
多边形的边不一定是水平或者竖直的,可以倾斜
题解:
首先由皮克定理
$$ S = a + \frac{b}{2} - 1$$( S 是多边形面积,a 是多边形内部格点数目,b 是多边形边界上的格点数目)
可知我们需要求得:多边形的面积和边界上格点的数目
-
对于两端点 (x1,y1),(x2,y2) 都再格点上的一条线段,该线段上的格点数目为\(\gcd(|x_1-x_2|,|y_1-y_2|)+1\)。
这很好理解,对于横坐标差值和纵坐标差值求得的最大公因数 g,相当于将横坐标差值分成 g 份,由于是整除,因此显然每份的左右端点都是整数,对于纵坐标也是同样的道理,由于是最大公因数,所以不可能再分更多份,因此 \(\gcd(|x_1-x_2|,|y_1-y_2|)\) 即求得两端点间最多能分成多少段由格点分割的线段,再加上 1 即整条线段上的格点数目。 -
对于格点按顺序给出的多边形,
设\(P_0 = P_{n+1}\) 且 O 为原点,则面积为\(\frac{1}{2} \sum_{i=0}^{n}{\left ( \overrightarrow{OP_i} \times \overrightarrow{OP_{i+1}} \right )}\)
这个画个图模拟一下也非常容易理解。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int maxn=1e5+10;
int n;
pll p[maxn];
inline ll gcd(ll m,ll n){return n?gcd(n,m%n):m;}
int main()
{
cin>>n;
for(int i=0;i<n;i++) scanf("%lld%lld",&p[i].first,&p[i].second);
ll S2=0, b=0;
for(int i=0;i<n;i++)
{
S2+=p[i].first*p[(i+1)%n].second-p[i].second*p[(i+1)%n].first;
b+=gcd(abs(p[i].first-p[(i+1)%n].first),abs(p[i].second-p[(i+1)%n].second));
}
cout<<(abs(S2)-b+2)/2<<endl;
}