【组合&取补集】数三角形 @CQOI2014/BZOJ3505/upcexam3843

http://exam.upc.edu.cn/problem.php?id=3843&csrf=8oK86t2oHSgi3Q4SX3qOJGeENe6pfXri
时间限制: 1 Sec 内存限制: 128 MB
题目描述
给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。
注意:三角形的三点不能共线。n×m的网格共有(n+1)×(m+1)个格点。
输入
输入一行,包含两个空格分隔的正整数m和n(1<=m,n<=1000)。
输出
输出一个正整数,为所求三角形数量。
样例输入
2 2
样例输出
76

取补集的思想,三角形的数量等于任取三点的情况减去三点共线的情况

#define FILE() freopen("../../in.txt","r",stdin)
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

ll combine(int n) { //n个点取3个
    ll m=n,res=1;
    for(int i=0; i<3; i++) {
        if(m%3==0) {
            res*=m/3;
        } else res*=m;
        m--;
    }
    return res/2;
}

int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}

int main() {
//    FILE();
    int n,m;
    cin>>n>>m;
    ll ans = combine((n+1)*(m+1))-(n+1)*combine(m+1)-(m+1)*combine(n+1); //先减去横竖格线上三点共线的
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) { //类似向量,从(0,0)到(i,j)作一线段 gcd(i,j)+1为线段上格点的数量
            ans-=(gcd(i,j)-1)*(n-i+1)*(m-j+1)*2; //乘2是因为沿y轴翻转情况一样
        }
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2018-03-21 12:26  NeilThang  阅读(111)  评论(0编辑  收藏  举报