暑训日记Day3 T2《数三角形》题解 (组合+容斥+gcd)

T2 数三角形

L u o g u Luogu Luogu 双倍经验:P3166

【问题描述】

Y Y Y手上有一个 n ∗ m n*m nm的网格,他想知道满足三个顶点都在格点上的三角形有多少个。

【输入格式】

输入一行,包含两个正整数 n n n m m m

【输出格式】

输出一行,包含一个正整数表示答案

【样例输入】

2 2

【样例输出】

76

【数据范围】

对于30%的数据,满足 n , m ≤ 10 n,m≤10 n,m10

对于100%的数据,满足 n , m ≤ 1000 n,m≤1000 n,m1000

【题解】

数学:组合 + 容斥 + g c d 数学:组合+容斥+gcd 数学:组合+容斥+gcd
∑ 三角形 = ∑ 三个点任意组合 − ∑ 三个点共线组合 \sum 三角形 = \sum三个点任意组合 - \sum三个点共线组合 三角形=三个点任意组合三个点共线组合

∑ 三角形 = C ( m + 1 ) ( n + 1 ) 3 − ∑ 三点横向排列 − ∑ 三点纵向排列 − ∑ 三点斜向排列 \sum 三角形 = C_{(m + 1)(n + 1)}^{3} - \sum三点横向排列 - \sum三点纵向排列 - \sum三点斜向排列 三角形=C(m+1)(n+1)3三点横向排列三点纵向排列三点斜向排列

∑ 三角形 = C ( m + 1 ) ( n + 1 ) 3 − ( m + 1 ) C n + 1 3 − ( n + 1 ) C m + 1 3 − 2 ∗ ∑ 三点斜向排列,且斜率为正 \sum 三角形 = C_{(m + 1)(n + 1)}^{3} - (m + 1)C_{n+1}^3 - (n + 1)C_{m+1}^3 - 2*\sum三点斜向排列,且斜率为正 三角形=C(m+1)(n+1)3(m+1)Cn+13(n+1)Cm+132三点斜向排列,且斜率为正

现在我们最大的问题就在于如何求出三个点斜向排列,且斜率为正的情况总数

给出一个特别的结论:

对于两个点坐标分别为 ( a , b ) 、 ( c , d ) (a,b)、(c,d) (a,b)(c,d),另两点横纵坐标差为 i = ∣ a − c ∣ , j = ∣ b − d ∣ i = |a - c|,j = |b - d| i=ac,j=bd,由于斜率已经被我们限制死了,所以 i = a − c , j = b − d i = a-c,j = b-d i=ac,j=bd,这两个点连线上的格点共有 g c d ( i , j ) − 1 gcd(i,j)-1 gcd(i,j)1 个,不难知道这样的点对共有 ( n − i + 1 ) ( m − j + 1 ) (n−i+1)(m−j+1) (ni+1)(mj+1)个,三点斜向排列,且斜率为正的组合共有:
∑ i = 1 n ∑ j = 1 m ( n − i + 1 ) ( m − j + 1 ) [ g c d ( i , j ) − 1 ] \sum_{i = 1}^n\sum_{j = 1}^m(n - i + 1)(m - j + 1)[gcd(i,j) - 1] i=1nj=1m(ni+1)(mj+1)[gcd(i,j)1]
上面的表达式注意要乘 2 2 2,这道题的最终结果即为:
C ( m + 1 ) ( n + 1 ) 3 − ( m + 1 ) C n + 1 3 − ( n + 1 ) C m + 1 3 − 2 ∗ ∑ i = 1 n ∑ j = 1 m ( n − i + 1 ) ( m − j + 1 ) [ g c d ( i , j ) − 1 ] C_{(m + 1)(n + 1)}^{3} - (m + 1)C_{n+1}^3 - (n + 1)C_{m+1}^3 -2*\sum_{i = 1}^n\sum_{j = 1}^m(n - i + 1)(m - j + 1)[gcd(i,j) - 1] C(m+1)(n+1)3(m+1)Cn+13(n+1)Cm+132i=1nj=1m(ni+1)(mj+1)[gcd(i,j)1]
算呗。反正这个数学题我当时没推出来 w w w www www。这道题在洛谷上是个省选难度的,放在 T 2 T2 T2不是很能理解。

#include<bits/stdc++.h>
#define REG register
#define LL long long
using namespace std;
LL n,m,ans,nm;
int main(){
  freopen("tri.in","r",stdin);
  freopen("tri.out","w",stdout);
  scanf("%lld %lld",&n,&m);
  ++n,++m; nm = n * m;
  ans = nm * (nm - 1) * (nm - 2) / 6 
      - n * m * (m - 1) * (m - 2) / 6 
	  - m * n * (n - 1) * (n - 2) / 6;
  for(REG int i = 1;i < n;i++)
	for(REG int j = 1; j < m ;j++)
	  ans -= (LL)2 * (LL)(__gcd(i , j) - 1)
	       * (LL)(n - i) * (LL)(m - j); 
  printf("%lld",ans);
  return 0;
} 

看完请留下你的痕迹 t h x thx thx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值