正题
题目链接:https://www.luogu.com.cn/problem/P3166
题目大意
求一个N∗MN*MN∗M的网格上有多少个三角形。
解题思路
考虑减去共线的情况,我们分为两种情况。一是平行于坐标轴的,这个很好算。二是倾斜的,我们考虑如何计算斜下角的。
首先我们可以枚举一个点作为左上角的点(x,y)(x,y)(x,y),对于一个在它右下角的点(x+a,y+a)(x+a,y+a)(x+a,y+a)在他们中间有gcd(a,b)gcd(a,b)gcd(a,b)个点和它们共线。当然除了gcd(1,1)=1gcd(1,1)=1gcd(1,1)=1的情况要减去。预处理gcdgcdgcd的二维前缀和就可以了。
时间复杂度O(nm)O(nm)O(nm)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1100;
ll n,m,g[N][N],ans;
ll gcd(ll x,ll y){if(!y)return x;if(g[x][y])return g[x][y];return gcd(y,x%y);
}
int main()
{scanf("%lld%lld",&n,&m);n++;m++;for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++)g[i][j]=gcd(i,j);for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++)g[i][j]+=g[i-1][j]+g[i][j-1]-g[i-1][j-1]-1;for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++){ans+=g[n-i][m-j];}ll k=n*m;printf("%lld",k*(k-1)*(k-2)/6-ans*2-m*n*(n-1)*(n-2)/6-n*m*(m-1)*(m-2)/6);
}