bzoj3505 [Cqoi2014]数三角形

乱搞 同时被 3 个专栏收录
14 篇文章 0 订阅
153 篇文章 0 订阅
6 篇文章 0 订阅

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


展开阅读全文
  • 1
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值