zoukankan      html  css  js  c++  java
  • BZOJ 3505 [Cqoi2014]数三角形

    3505: [Cqoi2014]数三角形

    Description

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

    Input

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

    Output

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

    Sample Input

    2 2

    Sample Output

    76

    HINT

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


      这肯定是很典型的排列组合水题。先n++,m++,再C(n*m,3),最后减去三点共线的特例。

      横的,竖的,很好算,C(n,3)*m+C(m,3)。但是斜的,需要想一想。

      在(a,b) (x,y)两点构成的线段上有gcd(a-x,b-y)-1个整点(a>x,b>y),这显然成立。斜的即有两点,△x与△y均大于0,在他们之间有gcd(△x,△y)-1个整点,而这种情况有2*(n-△x)*(m-△y)种。以O(n^2)的效率减去就是了。

     1 /**************************************************************
     2     Problem: 3505
     3     User: Doggu
     4     Language: C++
     5     Result: Accepted
     6     Time:660 ms
     7     Memory:820 kb
     8 ****************************************************************/
     9  
    10 #include <cstdio>
    11 #include <algorithm>
    12 long long n, m, x, ans;
    13 inline long long gcd(long long a,long long b) {return b==0?a:gcd(b,a%b);}
    14 inline long long C(long long p) {return p*(p-1)*(p-2)/6;}
    15 int main() {
    16     scanf("%lld%lld",&n,&m);n++;m++;x=std::min(n,m);
    17     ans=C(n*m)-C(n)*m-C(m)*n;
    18     for( int i = 1; i < n; i++ ) for( int j = 1; j < m; j++ ) ans-=(gcd(i,j)-1)*2*(n-i)*(m-j);
    19     printf("%lld
    ",ans);
    20     return 0;
    21 }
    22 
    math(820kb 660ms)

      但是,居然发现,若把gcd记忆化,效率将大大提高。

     1 /**************************************************************
     2     Problem: 3505
     3     User: Doggu
     4     Language: C++
     5     Result: Accepted
     6     Time:292 ms
     7     Memory:8792 kb
     8 ****************************************************************/
     9  
    10 #include <cstdio>
    11 #include <algorithm>
    12 long long n, m, x, ans, g[1010][1010];
    13 inline long long gcd(long long a,long long b) {if(g[a][b]) return g[a][b];return g[a][b]=b==0?a:gcd(b,a%b);}
    14 inline long long C(long long p) {return p*(p-1)*(p-2)/6;}
    15 int main() {
    16     scanf("%lld%lld",&n,&m);n++;m++;x=std::min(n,m);
    17     ans=C(n*m)-C(n)*m-C(m)*n;
    18     for( int i = 1; i < n; i++ ) for( int j = 1; j < m; j++ ) ans-=(gcd(i,j)-1)*2*(n-i)*(m-j);
    19     printf("%lld
    ",ans);
    20     return 0;
    21 }
    22 
    math++(8792kb 292ms)
  • 相关阅读:
    【2021-01-26】保持学习,也是一种科学的健康保养方式
    【2021-01-25】正常市场化的企业该有的现象
    day 42 mysql 数据库(2)
    day 41 数据库初学习
    在排序数组中查找元素的第一个和最后一个位置
    飞机游戏
    两个数组的交集 II
    重复N次的元素
    单词规律
    存在重复元素2
  • 原文地址:https://www.cnblogs.com/Doggu/p/bzoj3505.html
Copyright © 2011-2022 走看看