「题解」:数三角形

考拉学长杂题选讲最(水?)的一道题。

题面


题目描述

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

样例输入

2 2

样例输出

76

题解


直接求不现实。所以一点点容斥?

我们先求出来在全部的$t$个点$((n+1)*(m+1))$中任意选三个点的全部组合

$\frac{(t-2)*(t-1)*t}{3!}$ (解释:从t个数里选出第一个,剩下t-1个数中选第二个,t-2个数里选第三个,除以三个数的内部排列数)

然后我们尝试减去不合法的选择:

<1>.单行、单列上不合法的数量:与之前一样,只不过要在单行或者单列上选择。在单行上$\frac{(m+1)*m*(m-1)}{3!}$,推广到每一行,乘以行数,即为:$\frac{(m+1)*m*(m-1)}{3!}*(n+1)$,同理,在单列上为$\frac{(n+1)*n*(n-1)}{3!}$,推广到每一列,乘以列数,即为:$\frac{(n+1)*n*(n-1)}{3!}*(m+1)$。

<2>.斜着共线的三点的情况数量:设1点的坐标为$(x_1,y_1)$、$(x_2,y_2)$,则$gcd(x_1-x_2,y_1-y_2)-1$恰为点阵中1点和2点之间点的数量。一个非常显然的性质是:我们求出了一个斜线完全可以在点阵上平移,对称翻折。于是我们考虑优化:把其中一个点固定在左上角,然后枚举图中点的坐标,最后得出,左上角与枚举的端点相连所得的直线平移的可能总数为:$(n-x+1)*(m-y+1)$。于是得出公式:$(n-i+1)*(m-j+1)*2*(gcd(i,j)-1)$,两层for循环枚举i,j作为节点横纵坐标即可。

代码:

#include<iostream>
#include<cstdio>
#define rint register int
using namespace std;
long long n,m,t;
long long ans=0,row,line,inc;
inline int gcd(long long a,long long b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    scanf("%lld %lld",&m,&n);
    t=(m+1)*(n+1);
    ans=t*(t-1)*(t-2)/6;
    row=(n+1)*(m+1)*m*(m-1)/6;line=(m+1)*(n+1)*n*(n-1)/6;
    for(rint i=1;i<=n;++i)
        for(rint j=1;j<=m;++j)
            inc+=(n-i+1)*(m-j+1)*2*(gcd(i,j)-1);
    ans-=row;ans-=line;ans-=inc;
    cout<<ans<<endl;
    return 0;
}
View Code
posted @ 2019-07-23 11:46  hzoi_Joe  阅读(428)  评论(2编辑  收藏  举报