zoukankan      html  css  js  c++  java
  • [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
  • 相关阅读:
    PHP 表单
    php之表单-2(表单验证)
    go语言使用官方的 log package 来记录日志
    golang 中timer,ticker 的使用
    go语言slice的理解
    GETTING STARTED WITH THE OTTO JAVASCRIPT INTERPRETER
    golang time.Duration()的问题解疑
    css3动画
    【转】golang中的并行与并发
    【转】Golang 关于通道 Chan 详解
  • 原文地址:https://www.cnblogs.com/AK-ls/p/11727343.html
Copyright © 2011-2022 走看看