bzoj 3505 数三角形 - 组合数学

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

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output

输出一个正整数,为所求三角形数量。

Sample Input

2 2

Sample Output

76

Hint

数据范围
1<=m,n<=1000

  不难得到一个思路。最终的答案 = 任选3点的方案数 - 三点共线的方案数。

  前者很好求直接组合数就好了。后者可以枚举线段两端点,然后计算第三个点在这个线段上(不含端点)的方案数,这要用到在网格图中一个神奇的结论,两个格点点的连线穿过的格点数等于这两点的横纵坐标之差的最大公约数减一,至于它的证明可以用相似再加一点数论的东西。但是枚举任意两点会超时。但发现很多地方其实这个横纵坐标的差是相等的,所以直接枚举这个横纵坐标之差,然后乘法乘一乘就好了。

Code

 

 1 /**
 2  * bzoj
 3  * Problem#3505
 4  * Accepted
 5  * Time:344ms
 6  * Memory:1288k
 7  */
 8 #include<iostream>
 9 #include<cstdio>
10 #include<ctime>
11 #include<cctype>
12 #include<cstring>
13 #include<cstdlib>
14 #include<fstream>
15 #include<sstream>
16 #include<algorithm>
17 #include<map>
18 #include<set>
19 #include<stack>
20 #include<queue>
21 #include<vector>
22 #include<stack>
23 #ifndef WIN32
24 #define Auto "%lld"
25 #else
26 #define Auto "%I64d"
27 #endif
28 using namespace std;
29 typedef bool boolean;
30 const signed int inf = (signed)((1u << 31) - 1);
31 #define smin(a, b) a = min(a, b)
32 #define smax(a, b) a = max(a, b)
33 #define max3(a, b, c) max(a, max(b, c))
34 #define min3(a, b, c) min(a, min(b, c))
35 template<typename T>
36 inline boolean readInteger(T& u){
37     char x;
38     int aFlag = 1;
39     while(!isdigit((x = getchar())) && x != '-' && x != -1);
40     if(x == -1) {
41         ungetc(x, stdin);
42         return false;
43     }
44     if(x == '-'){
45         x = getchar();
46         aFlag = -1;
47     }
48     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
49     ungetc(x, stdin);
50     u *= aFlag;
51     return true;
52 }
53 
54 template<typename T>
55 T gcd(T a, T b) {
56     if(!b)    return a;
57     return gcd(b, a % b);
58 }
59 
60 int n, m;
61 int pro;
62 long long res;
63 
64 inline void init() {
65     readInteger(n);
66     readInteger(m);
67     pro = (n + 1) * (m + 1);
68     res = pro  * 1LL * (pro - 1) * (pro - 2) / 6;
69 }
70 
71 inline void solve() {
72     for(int i = 0; i <= n; i++) {
73         for(int j = 0; j <= m; j++) {
74             if(i == 0 && j == 0)    continue;
75             long long t = (gcd(i, j) - 1LL) * (n - i + 1) * (m - j + 1);
76             if(!i || !j) res -= t;
77             else res -= (t << 1);
78         }
79     }
80     printf(Auto, res);
81 }
82 
83 int main() {
84     init();
85     solve();
86     return 0;
87 }
posted @ 2017-07-06 09:53  阿波罗2003  阅读(235)  评论(0编辑  收藏  举报