Uva 12075 - Counting Triangles 解题报告

96 篇文章 0 订阅

Triangles are polygons with three sides and strictly positive area. Lattice triangles are the triangles all whose vertexes have integer coordinates. In this problem you have to find the number of lattice triangles in an M x Ngrid. For example in a (1 x 2) grid there are 18 different lattice triangles as shown in the picture below:

\epsfbox{p3295.eps}

Input 

The input file contains at most 21 sets of inputs.

Each set of input consists of two integers M and N ( 0 < MN$ \le$1000 ). These two integers denote that you have to count triangles in an (M x N) grid.

Input is terminated by a case where the value of M and N are zero. This case should not be processed.

Output 

For each set of input produce one line of output. This output contains the serial of output followed by the number lattice triangles in the (M x N) grid. You can assume that number of triangles will fit in a 64-bit signed integer.

Sample Input 

1 1
1 2
0 0

Sample Output 

Case 1: 4
Case 2: 18

    解题报告: 好吧,这题我看了解题报告。想了好几天,却卡在一个地方。我的想法是: 求三角形的数量,可以用C(m*n, 3)减去所有在同一条直线上的三点的情况数。枚举斜率求所有的情况,然后卡在这里……

    这题和Highway那一题其实很像。我们需要求出所有三点共线的情况数。何时三点共线?当第三个点(x3,y3)与第一个点(x1,y1)的斜率和第二个点(x2,y2)与第一个点的斜率相同时。

    换句话说,以(0,0)为起点,(2,2)点与(0,0),(1,1)点三点共线,因为gcd(2,2)=2,说明中间有一个点。

    我们固定起始点和终点(保证无重复),计算当中间点不同时有多少种三点共线的情况,然后累加,得到(i,j)点中所有三点共线的情况。再累加,得到(0,0)到(i,j)点的点阵中所有的三点共线的情况。对称的原因,结果乘2。代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
LL sum[1111][1111];

LL C(LL n, LL m)
{
    if(n<m) return 0;

    LL res=1;
    for(int i=0;i<m;i++)
        res*=(n-i), res/=(i+1);
    return res;
}

int gcd(int a, int b)
{
    return b==0?a:gcd(b, a%b);
}

void init()
{
    for(int i=2;i<=1000;i++)
        for(int j=2;j<=1000;j++)
            sum[i][j]=gcd(i, j)-1;

    for(int i=2;i<=1000;i++)
        for(int j=2;j<=1000;j++)
            sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+sum[i][j];

    for(int i=2;i<=1000;i++)
        for(int j=2;j<=1000;j++)
            sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+sum[i][j];
}

int cas=1;
void work(int n, int m)
{
    printf("Case %d: %lld\n", cas++, C(m*n, 3) - C(n, 3)*m - C(m, 3)*n - sum[n-1][m-1]*2);
}

int main()
{
    init();

    int m, n;
    while(~scanf("%d%d", &n, &m) && (m||n))
        work(n+1, m+1);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值