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;
}

 

posted @ 2017-08-24 14:17  chunlvxiong  阅读(172)  评论(0编辑  收藏  举报