【洛谷P3166】数三角形

题目

题目链接:https://www.luogu.com.cn/problem/P3166
给定一个 \(N\times M\) 的网格,请计算三点都在格点上的三角形共有多少个。注意三角形的三点不能共线。

思路

考虑总方案数减去三点共线的方案数。枚举每一个点 \((x,y)\),然后再枚举在 \((x,y)\) 左下角的一个点 \((k,l)\),那么在这两个点之间的点有 \(\gcd(i-k,j-l)-1\) 个。
但是当这三个点所构成直线斜率为负数的时候,方案数我们不会计算到,由于每一个斜率为正数的直线旋转 \(90°\) 都可以得到一条斜率为负数的直线,所以 \(ans\) 直接乘上 \(2\),然后减去三点在同一行或同一列的方案数。
后者可以直接 \(O(nm)\) 组合数暴力求出,对于 \(ans\) 直接预处理 \(\gcd\) 的前缀和乱搞即可。
时间复杂度 \(O(nm)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll N=1010;
ll n,m,ans,sum[N][N];

ll gcd(ll x,ll y)
{
	if (!y) return x;
	return gcd(y,x%y);
}

int main()
{
	scanf("%lld%lld",&n,&m);
	n++; m++;
	for (ll i=0;i<=max(n,m);i++)
		sum[i][0]=sum[0][i]=sum[0][i-1]+i;
	for (ll i=1;i<=n;i++)
		for (ll j=1;j<=m;j++)
		{
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+1LL*gcd(i,j);
			ans+=sum[i-1][j-1]-i*j+1LL;
		}
	ans*=2LL;
	for (ll i=1;i<=n;i++)
		for (ll j=1;j<=m;j++)
			ans-=((i-2LL)*(i-1LL)/2LL+(j-2LL)*(j-1LL)/2LL);
	ll cnt=n*m;
	printf("%lld",cnt*(cnt-1)*(cnt-2)/6-ans);
	return 0;
}
posted @ 2020-09-28 21:51  stoorz  阅读(166)  评论(0编辑  收藏  举报