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
  • 相关阅读:
    Candy leetcode java
    Trapping Rain Water leetcode java
    Best Time to Buy and Sell Stock III leetcode java
    Best Time to Buy and Sell Stock II leetcode java
    Best Time to Buy and Sell Stock leetcode java
    Maximum Subarray leetcode java
    Word Break II leetcode java
    Word Break leetcode java
    Anagrams leetcode java
    Clone Graph leetcode java(DFS and BFS 基础)
  • 原文地址:https://www.cnblogs.com/AK-ls/p/11727343.html
Copyright © 2011-2022 走看看