链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3227
题解
三角形数量不好统计,可以统计三点共线情形的数量,再用总数
C
(
n
+
1
)
(
m
+
1
)
3
C_{(n+1)(m+1)}^3
C(n+1)(m+1)3减去
首先如果我选出的三个点形成的直线恰好是水平或者竖直的,这种情形的数目是
n
C
m
+
1
3
+
m
C
n
+
1
3
nC_{m+1}^3+mC_{n+1}^3
nCm+13+mCn+13
剩下的情形就是斜线,我可以先只统计从左上到右下的这种情形,最后乘以2得到总的数目
我先枚举右下角的点,然后对于每个右下角的点统计其能和左上方多少点对构成直线,设右下角这个点是
(
i
,
j
)
(i,j)
(i,j),那么我其实就是在一个长为
i
i
i宽为
j
j
j的矩形中统计能和右下角形成直线的点对个数
不妨对称一下,统计和左上角连线为直线的点对个数,这两个问题的答案是相同的,设为
f
i
j
f_{ij}
fij
那么
f
i
j
=
f
i
−
1
,
j
+
f
i
,
j
−
1
−
f
i
−
1.
j
−
1
+
C
g
c
d
(
i
,
j
)
2
−
C
g
c
d
(
i
,
j
)
−
1
2
f_{ij}=f_{i-1,j}+f_{i,j-1}-f_{i-1.j-1}+C_{gcd(i,j)}^2-C_{gcd(i,j)-1}^2
fij=fi−1,j+fi,j−1−fi−1.j−1+Cgcd(i,j)2−Cgcd(i,j)−12
如果不理解上式,可以看我的这篇文章
https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/51346126
最后对
f
i
j
f_{ij}
fij求个前缀和
g
i
j
g_{ij}
gij,再乘以
2
2
2,即可得到构成斜线的情况个数
答案就是
C
(
n
+
1
)
(
m
+
1
)
3
−
2
g
n
m
−
n
C
m
+
1
3
+
m
C
n
+
1
3
C_{(n+1)(m+1)}^3-2g_{nm}-nC_{m+1}^3+mC_{n+1}^3
C(n+1)(m+1)3−2gnm−nCm+13+mCn+13
代码
//递推
#include <bits/stdc++.h>
#define ll long long
#define maxn 1010
using namespace std;
ll f[maxn][maxn], g[maxn][maxn];
ll gcd(ll a, ll b){return !b?a:gcd(b,a%b);}
ll C(ll n, ll m)
{
if(m>n)return 0;
ll i, ans=1;
for(i=n;i>=n-m+1;i--)ans*=i;
for(i=1;i<=m;i++)ans/=i;
return ans;
}
int main()
{
ll i, j, N, M, kase=0;
for(i=1;i<maxn;i++)for(j=1;j<maxn;j++)
f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+C(gcd(i,j),2)-C(gcd(i,j)-1,2),
g[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1]+f[i][j];
while(scanf("%lld%lld",&N,&M),N)printf("Case %lld: %lld\n",++kase,C((N+1)*(M+1),3)-g[N][M]*2-(N+1)*C(M+1,3)-(M+1)*C(N+1,3));
return 0;
}