[CQOI2014]数三角形

题目描述

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

输入格式

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

输出格式

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


  emmmmmmmm, 刚看到这道题, 这岂不是组合数, 一个n * m的网格上一共有(n + 1) * (m + 1) 个节点, 那么选择的方案数显然是$C^3_{(n+ 1)*(m+ 1)}$, 又因为组成三角形, 肯定三点不能共线,  根据容斥的原理, 肯定要减去不合法的。 .当三点在横轴和竖轴上的时候, 显然就是减去$(m +1) * C^3_{n + 1}$和$(n+1) * C^3_{m+1}$, 那我们来考虑斜着的情况.
  在一个坐标轴中, 一条直线的斜率可以是正的或者负的, 由于矩形的对称性, 我们只考虑斜率大于0的情况乘以2就行了。
假设我们固定了一个点, 我们可以暴力$O(n^2)$的枚举每一个点, 因为已经固定两个点, 那么与这两点之间的点与这两点就组成了一条直线,就要减去这种情况, 那么怎么求这两点之间的整数点呢??? 
  我们可以把(x, y)分段, 显然可知, 当把(x, y)分成相同的段数时, 这条直线上的点都是整数点(那我就不证明了。。);
  假设我们把(x,y)分成a段, 因为每一段对应直线上的点都是一个整点, 所以除去两端的点直线上的点就是a - 1,我们要找出最多的点, 就要找出最大的a, 因为a | x, a | y, 当a == gcd(x, y); 此时的a最大, 就包含了所有的情况。
  这样你枚举固定的点, 又暴力枚举每个点, 这样的复杂度显然是不现实的, 我们假设选择的点是(0, 0)和一个点(x, y), 那么在这个矩阵上存在和这条直线平行(或重合)且长度相等的直线就有$(m - j+1) * (n - i +1)$个 (横着平移的话有$m - j +1$个, 竖着平移有$n - i +1$个), 所以我们就假设已经固定了(0, 0)点, 暴力枚举每一个点, 直接减去所有等效的直线, 最终复杂度是$O(nm)$
 
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 100;
const int MAXM = 3e3 + 10;
const double eps = 1e-5;

template < typename T > inline void read(T &x) {
    x = 0; T ff = 1, ch = getchar();
    while (!isdigit(ch)) {
        if(ch == '-') ff = -1;
        ch = getchar();
    }
    while (isdigit(ch)) { 
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= ff;
}

template < typename T > inline void write(T x) {
    if (x == 0) {
        putchar('0');
        return ; 
    }
    if (x < 0) putchar('-'), x = -x;
    static T tot = 0, ch[30];
    while (x) {
        ch[++tot] = x % 10 + '0';
        x /= 10;
    }
    while (tot) putchar(ch[tot--]);
} 

ll n, m, ans;

inline ll C(ll x) {
    return x * (x - 1) * (x - 2) / 6;
}

inline int gcd(int x, int y) {
    if (x == 0) return y;
    return gcd(y % x, x);
}

int main() {
    read(n); read(m); 
    ans = C((n + 1) * (m + 1));
    ans -= (n + 1) * C(m + 1);
    ans -= (m + 1) * C(n + 1);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            ans -= 2ll * (gcd(i, j) - 1) * (m - j + 1) * (n - i + 1);
        }
    }
    write(ans);
    return 0;
}
View Code

 

posted @ 2019-10-23 18:08  海边微风起  阅读(213)  评论(0编辑  收藏  举报