zoukankan      html  css  js  c++  java
  • Luogu3166 [CQOI2014]数三角形(排列组合)题解

    结论题

    先给结论吧:

    一个两直角边分别为(x)(y)的格点直角三角形(即在网格图上),其斜边上的整点数量为(gcd(x,y)+1)(包含两端点).

    貌似看完结论还是不会呢

    思路

    首先声明:本题是格点图,所以要把行数列数都加上(1)

    先简单容斥一下,合法三角形数量(=)从格点图上任选三个点的方案数(-)选出的三点共线的方案数。

    • 任选三点:(C(n*m,3))

    • 三点共线:

      • 横线:共有(n)行,每行都是从(m)个数中选(3)个,即为(C(m,3)*n)

      • 竖线:共有(m)列,每列都是从(n)个数中选(3)个,即为(C(n,3)*m)

      • 斜线:斜率为正的斜线与斜率为负的斜线是等价的,故放在一起考虑。根据先前的结论,我们可以枚举两点之间行与列的差值(i)(j),那么第三个点就可以选择前两个点之间连线上的任意一个整点,即有(gcd(i,j)-1)种方案(点与点不能重合)。所以斜线的方案总数为:

        [2 imes sum_{i=1}^{n}sum_{j=1}^{m}{(n-i) imes(m-j) imes (gcd(i,j)-1)} ]

    最后作差即可。

    Tips

    这里求组合数需要高效的(O(n))算法,即:

    (C(n,m)=C(n,m-1)*(n-m+1)/m)

    边界条件:(C(n,0)=1)。(注意要先乘后除

    事实上,本题还有(O(n))的算法,有兴趣的同学可以自行了解。

    参考代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define LL long long
    
    using namespace std;
    
    int n,m;
    LL Ans;
    
    LL C(int x, int y){
        if(y > x) return 0;
        if(y == 0 || y == x) return 1;
        if(y == 1) return x;
        if(y > x - y) y = x - y;
        return (LL)(x - y + 1) * C(x, y - 1) / y;
    }
    
    int gcd(int x, int y){
        if(y == 0) return x;
        return gcd(y, x % y);
    }
    
    int main(){
        scanf("%d%d", &n, &m); n += 1, m += 1;
        Ans = C(m * n, 3);
        Ans -= C(m, 3) * n + C(n, 3) * m;
        LL f = 0;
        for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= m; ++ j){
                f += (n - i) * (m - j) * (gcd(i, j) - 1);
            }
        printf("%lld
    ", Ans - f * 2);
        return 0;
    }
    
  • 相关阅读:
    Matlab命令集常用字符串函数
    统计独立性和统计相关性
    查看solaris下硬盘的物理大小
    Perl命令行开关
    DateFormat.getDateInstance出现Unparseable date
    ActiveMQ 本地转本地再转远程的完整配置
    aspose.words 操作word生成试卷
    汇编in和out介绍
    eclipse开发国际化项目利器:MultiProperties
    RCP+GEF+界面开发(2)[eclipse插件配置LINK方法]
  • 原文地址:https://www.cnblogs.com/whenc/p/13988989.html
Copyright © 2011-2022 走看看