[CQOI2014]数三角形 组合数 + 容斥 + gcd

推导过程 : 组合数+容斥原理+gcd

正确做法是暴力的一种优化,ans=所有情况 - 平行坐标轴的三点共线 - 斜线三点共线

如果快速求斜线三点共线:

首先要知道一个结论,对于点(a,b) (x,y)连成的线段而言(其中a>x,b>y),

在它们中间有gcd(a-x,b-x)-1个整点,因此基本的思路就是枚举两个点,

然后第3个点就是gcd(a-x,b-x)-1种可能了

至于为什么第3个点一定要在中间,是为了保证不重不漏,只用两边的点统计中间的点,

然而这样复杂度太高,于是可以发现,可以将这两个点组成的线段中左下那个端点平移至原点,

这样相当于只要枚举一个点,并且由于要考虑k<0的情况,因为矩形是有对称性的,

所以要求原点+一个点 与 (0,m)+一个点 的和就可以直接2 *(原点+一个点)

由于长的一样的线有很多,于是问题就转化为如果求这些一样的线的个数,

那么可以发现,这样任意一条线,向上只能平移(n - i),向下(m - j)次,

所以可能性就为(n - i + 1) * (m - j + 1),其中+1是因为可以向上移动0个单位

但由于这里n,m一开始就加了1,所以这个式子就不用+1了

因此枚举每个点即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define LL long long
 5 LL n,m,ans,go;
 6 
 7 int gcd(int x,int y)
 8 {
 9     if(!y) return x;
10     else return gcd(y,x%y);
11 }
12 
13 void work()
14 {
15     scanf("%lld%lld",&n,&m);
16     ++n,++m;//因为是一个网格,所以真正的坐标系其实有(n+1,m+1)
17     go=n*m;
18     ans=go * (go - 1) * (go - 2) / 6 - n * m * (m - 1) * (m - 2) / 6 - m * n * (n - 1) * (n - 2) / 6;//记得除掉取出数列的全排列
19     for(R i=1; i<n ;i++)//因为是取了原点,所以相当于坐标系是从0开始了
20         for(R j=1; j<m ;j++)//枚举这个点
21             ans-=(LL)2 * (LL)(gcd(i,j) - 1) * (LL)(n - i) * (LL)(m - j); 
22     printf("%lld
",ans);
23 }
24 
25 int main()
26 {
27     freopen("in.in","r",stdin);
28     work();
29     fclose(stdin);
30     return 0;
31 }
原文地址:https://www.cnblogs.com/ww3113306/p/8762942.html