洛谷 P3166 [CQOI2014]数三角形
BZOJ 3505 [Cqoi2014]数三角形
题目链接:洛谷 P3166 [CQOI2014]数三角形 BZOJ 3505 [Cqoi2014]数三角形
算法标签: 组合数学
、最大公约数
题目
题目描述
给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。
注意三角形的三点不能共线。
输入格式
输入一行,包含两个空格分隔的正整数m和n。
输出格式
输出一个正整数,为所求三角形数量。
输入输出样例
输入 #1
2 2
输出 #1
76
说明/提示
数据范围
bzoj上是1<=m,n<=1000
题解:
组合数学
首先对于这道题的读入,n和m是从0开始计算的,为了方便处理,不妨将n与m都++。
我们可以先考虑去枚举每一个三角形,很显然是做不到的。
那么我们可以换一种思路,我们可以确定的是每一个三角形一定有三个点,并且这三个点不共线。
对于一定有三个点,很显然我们可以得到总答案:(ans~=~C_{n*m}^{3})
那么对于三点不共线,我们现在就要考虑以下的情况:
1.三点不共横线:很显然,我们枚举每一条横边,在上边任选三个点,这样的三个点都是不合法的,又因为有n条横边,所以(ans~-=~n imes C_{m}^{3})
2.三点不共竖线:同上,枚举每一条竖边,任选三点除去,所以(ans~-=~m imes C_{n}^{3})
3.三点不过斜线:如何判断斜线,这就是这道题的难点所在,分析后我们可以发现一个问题,对于一个((x imes y))的网格矩形,从最左上角点到最右下角点的直线会经过(gcd(x,y)-1)个格点(不包含起点和中点),同理从最右上角点到最左下角点也会经过(gcd(x, y)-1)个格点,那么我们只需要枚举所有的矩形,删去这些情况即可:(ans~-=~(n-i)*(m-j)*(gcd(i,j)-1)*2)
最终得到的ans就是答案。
!!!注意:这道题的数据需要开(long long)
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, ans;
ll gcd(ll a,ll b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int main()
{
scanf("%lld%lld", &m ,&n);
n ++ ;
m ++ ;
ans = (n * m) * (n * m - 1) * (n * m - 2) / (1 * 2 * 3);
ans -= n * m * (m - 1) * (m - 2) / (1 * 2 * 3);
ans -= m * n * (n - 1) * (n - 2) / (1 * 2 * 3);
for (ll i = 2; i < n; i ++ )
for (ll j = 2; j < m; j ++ )
{
ans -= (n - i) * (m - j) * (gcd(i, j) - 1) * 2;
}
printf("%lld
", ans);
return 0;
}