zhber
有好多做过的题没写下来,如果我还能记得就补吧

 

Description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output


输出一个正整数,为所求三角形数量。

Sample Input


2 2

Sample Output

76


数据范围
1<=m,n<=1000

 

一开始不会做……orz了各路神犇之后才有了思路

首先答案就是所有取出三个点的方案数减去会三点共线的方案数

显然n*m的网格上有(n+1)*(m+1)个整点,然后令t=(n+1)*(m+1),那么取三个点的方案数就是t*(t-1)*(t-2)/6(就是排列组合啦T T)

然后要考虑怎么算三点共线的方案数

然后有一个结论是在(a,b) (x,y)两点构成的线段上有gcd(a-x,b-y)-1个整点(a>x,b>y)

我们固定(a,b) (x,y)为共线的三点中左边两个,那么第三个点的方案数就是gcd(a-x,b-y)-1

但是这样枚举abxy的复杂度是O(n^2*m^2)

优化是把这线段平移到原点处,那么会发现其实只要枚举(0,0) (x-a+1,y-b+1),其他的线段平移就可以了(这里强烈建议自己动手画一画!

画完很容易发现这样(0,0) (x-a+1,y-b+1)的线段可以平移出(n-i+1)*(m-j+1)种不同方案,在 (x-a+1,y-b+1)的时候如果不在坐标轴上还要算两次

另外,先打表算gcd竟然能这么快……

#include<cstdio>
#define LL long long
int gcd[1010][1010];
int n,m;
LL t,ans;
inline int getgcd(int a,int b)
{
	if (gcd[a][b])return gcd[a][b];
	if (!a)return gcd[a][b]=b;
	if (!b)return gcd[a][b]=a;
	return gcd[a][b]=getgcd(b,a%b);
}
inline void calc()
{
	for(int i=1;i<=m;i++)gcd[0][i]=i;
	for(int i=1;i<=n;i++)gcd[i][0]=i;
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	    getgcd(i,j);
}
int main()
{
	scanf("%d%d",&n,&m);
	calc();
	t=(n+1)*(m+1);
	ans=t*(t-1)*(t-2)/6;
	for (int i=0;i<=n;i++)
	  for (int j=0;j<=m;j++)
	    if (i||j)
	    {
	    	if (!i||!j)ans-=(LL)(gcd[i][j]-1)*(n-i+1)*(m-j+1);
	    	else ans-=(LL)2*(gcd[i][j]-1)*(n-i+1)*(m-j+1);
	    }
	printf("%lld",ans);
}

  

posted on 2014-08-10 22:02  zhber  阅读(363)  评论(0编辑  收藏  举报