UVA12075 - Counting Triangles

链接

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=fi1,j+fi,j1fi1.j1+Cgcd(i,j)2Cgcd(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)32gnmnCm+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;
}
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值