USACO3.4.2--Electric Fences
chunlvxiong的博客
题目描述:
一个三角形(0,0)-->(n,m)-->(p,0)(0<=n<32000,0<m<32000,0<p<32000)。求出三角形中的格点数(边界的不算)。
思考&分析:
一个较简单的做法:由于格点的横纵坐标都是整数,不妨枚举格点所有可能的纵坐标,然后计算出格点的左边界和右边界,相减得到纵坐标一定时格点数并累加。具体计算左边界和右边界详见代码。
皮克定理:对于格点多边形,有2S=2a+b-2,其中a表示格点多边形内部的点数,b表示格点多边形边界上的点数,S表示格点多边形的面积。
显然S容易计算,因此只需要计算b就可以得到a也就是问题的答案了。底边的格点数显然容易计算。
考虑(0,0)到(a,b)的一条直线,其中的格点数为gcd(a,b)(不包括点(0,0)),证明如下:
考虑格点(x,y)在(a,b)上,那么x/y=a/b,即bx=ay,这个等式最小成立的(x,y)即x=lcm(a,b)/b,y=lcm(a,b)/a,最大成立的(x,y)即(a,b)。所以总的格点数为lcm(a,b)/b/a=gcd(a,b)个。
因此可以计算出另两条直线的格点数,也就可以快速解决该问题。
贴代码:
O(M)做法:
#include <bits/stdc++.h> using namespace std; int n,m,p; int main(){ freopen("fence9.in","r",stdin); freopen("fence9.out","w",stdout); scanf("%d%d%d",&n,&m,&p); int res=0; for (int i=1,L,R;i<m;i++){ L=i*n/m+1; if (p>=n) R=p-i*(p-n)/m-1; else if (i*(n-p)%m==0) R=p+i*(n-p)/m-1; else R=p+i*(n-p)/m; if (L<=R) res+=R-L+1; } printf("%d\n",res); return 0; }
皮克定理:
#include <bits/stdc++.h> using namespace std; int n,m,p; int gcd(int a,int b){ if (!b) return a; else return gcd(b,a%b); } int main(){ freopen("fence9.in","r",stdin); freopen("fence9.out","w",stdout); scanf("%d%d%d",&n,&m,&p); int S=p*m; int b=p+gcd(m,n)+gcd(m,abs(n-p)); printf("%d\n",(S+2-b)/2); return 0; }