[CQOI2014]数三角形(组合数)

题目

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。注意三角形的三点不能共线。

题解

  • 首先所有的方案数为 C ( n + 1 ) × ( m + 1 ) 3 C_{(n+1)\times(m+1)}^3 C(n+1)×(m+1)3
  • 那么我们考虑什么时候三个点构不成三角形——三点共线
  • 当三点分别平行于 x x x轴, y y y轴共线时方案数为 ( n + 1 ) × C m + 1 3 (n+1)\times C_{m+1}^{3} (n+1)×Cm+13 ( m + 1 ) × C n + 1 3 (m+1)\times C_{n+1}^{3} (m+1)×Cn+13
  • 我们考虑斜着三点共线的情况
    • 对于两点 ( x 1 , y 1 ) , ( x 2 , y 2 ) ( x 2 > x 1 , y 2 > y 1 ) (x_1,y_1),(x_2,y_2)(x_2>x_1,y_2>y_1) (x1,y1),(x2,y2)(x2>x1,y2>y1)他们之间的整点个数为 g c d ( x 2 − x 1 , y 2 − y 1 ) − 1 gcd(x_2-x_1,y_2-y_1)-1 gcd(x2x1,y2y1)1
    • 那么我们肯定不能枚举 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2这样复杂度是 O ( n 4 ) O(n^4) O(n4),我们考虑将一个点一到原点,因为所有长度相同的线段,所贡献的答案是一样的,我们只需考虑有几条长度相同的线段即可。 那我们考虑长度相同的线段有几条。假设另一端点为 ( i , j ) (i,j) (i,j)(一个端点为 ( 0 , 0 ) (0,0) (0,0)),那么条数为 ( n − i + 1 ) × ( m − j + 1 ) (n-i+1)\times (m-j+1) (ni+1)×(mj+1)(画个图就很好理解了),那么不合法的状态就为 ( g c d ( i , j ) − 1 ) × ( n − i + 1 ) × ( m − j + 1 ) (gcd(i,j)-1) \times (n-i+1)\times (m-j+1) (gcd(i,j)1)×(ni+1)×(mj+1)
  • 用所有方案数减去不合法的方案数即可(这种思想似乎叫做补集转化?)

code

#include <bits/stdc++.h> 

using namespace std; 

typedef long long LL; 
const int N = 1e3 + 100; 

int m, n; 

inline int gcd(int x, int y) {
	return y ? gcd(y, x % y) : x; 
}

int main() {
	scanf("%d%d", &m, &n); m++, n++; 
	int all = m * n; 
	LL ans = 1ll * all * (all - 1) * (all - 2) / 6; 
	if (m >= 3) ans -= 1ll * n * m * (m - 1) * (m - 2) / 6; 
	if (n >= 3) ans -= 1ll * m * n * (n - 1) * (n - 2) / 6; 
	// cout << ans << endl; 
	for (int i = 1; i < n; ++i) {
		for (int j = 1; j < m; ++j) {
			ans -= 2 * 1ll * (LL)(gcd(i, j) - 1) * (LL)(n - i) * (LL)(m - j); 
		}
	}
	printf("%lld\n", ans);
 	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值