时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld
题号 NC19934
名称 [CQOI2014]数三角形
来源 [CQOI2014]
题目描述
给定一个 n×m的网格,请计算三点都在格点上的三角形共有多少个。
注意:三角形的三点不能共线。
样例
输入: 2 2 输出: 76
算法1
(容斥原理 + 组合计数)
- 三个点确定一个三角形,我们求出从个点中选择三个点的组合数 - 三点构成直线的数量(容斥原理)
如上图:任意一条两个端点在整点上的线段,其覆盖的整点数量为
对于不同长度线段(两个端点在整点上)都对应到一个长或高不同的直角三角形
对于定长的线段我们可以通过将两个点固定在线段的两个端点,另一个点从中间的点中选来得到,方案数就为
除了长度不同,我们还可以通过平移来得到不同的线段
斜率为正的线段和斜率为负的线段个数是相等的
用记录去掉水平和铅锤线段的方案数
然后我们用的循环枚举直角三角形的直角边长,计算直角三角形对应线段的方案的个数
答案就为
时间复杂度
参考文献
C++ 代码
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; int n,m; LL C(int a) { return 1ll * a * (a - 1) * (a - 2) / 6; } int main() { scanf("%d%d",&n,&m); n ++,m ++;//点的数量是长度 + 1 LL res = C(n * m) - n * C(m) - m * C(n);//总方案数减去水平的线段和铅锤的线段 for(int i = 1;i < n;i ++) for(int j = 1;j < m;j ++) res -= 2ll * (__gcd(i,j) - 1) * (n - i) * (m - j); //减去不同长度的线段的方案数,* 2是计算不同斜率的线段,* (n - i) * (m - j)计算平移后的直线段个数 printf("%lld\n",res); return 0; }