P3166 数三角形

题面

计数题,听妙的。

首先n ++,m ++;因为是格点数,题目给的是边长。

考虑容斥,所有可能组成三角形的个数就是任意取三个点即C(nm,3)减去不合法的就是在同一条直线上的。

同一条直线分为横竖斜。横竖直接考虑C(n,3)*m和C(m,3)*n

斜边稍微复杂一点,画个图。

我们考虑所有不合法的直线,两点确定一条直线,所以枚举两个点,复杂度O(n^2m^2)死掉了,把其中一个点等价转换到坐标系(0,0)点处,枚举另一个点。

显然在这两条直线间经过的格点数目为gcd(i,j)-1。考虑如果gcd(i,j)==1,那么两点中间就是直接到达的。用反证法,如果gcd(i,j)!=1且中间没有点,那么(i/gcvd(i,j),j/gcd(i,j))这个点必定会挡住线段的两个端点,所以不成立。即两点之间格点数目为(gcd(i,j)-1)。当前i,j考虑的点由于两个端点固定,可以把他进行等价平移,平移个数就是(n-i)*(m-j),最后因为矩阵对称性,现在的直线相当于斜率为负,考虑斜率是正的时候*2,就好了。

 代码简单

#include <bits/stdc++.h>

using namespace std;

#define int long long
const int N = 1e3 + 10;

int n, m;
int a[N][N], cnt;
int q[N<<2][2];
int gcd(int a,int b){
    if(b == 0) return a;
    return gcd(b, a % b);
}

int sss;
signed main(){
    cin >> n >> m;
    n ++;m ++;
    int ans = n * m;
    ans = ans * (ans - 1) * (ans - 2) / 6;
    ans = ans - (m * n * (n - 1) * (n - 2) / 6);
    ans = ans - (n * m * (m - 1) * (m - 2) / 6);
    for(int i = 1;i < n;++ i){
        for(int j = 1;j < m;++ j){
            ans -= 2 * (gcd(i,j)-1) * (n - i) * (m - j);
        }
    }
    cout << ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Shu-Kuang/p/13378512.html