1. 程式人生 > >BSOJ 3899 -- 【CQOI2014】 數三角形

BSOJ 3899 -- 【CQOI2014】 數三角形

Description

  給定一個n*m的網格,請計算三個點都在格點上的三角形共有多少個。下圖為4*4的網格上的一個三角形。               注意三角形的三點不能共線。

Input

輸入一行,包含兩個空格分隔的正整數m和n。

Output

輸出一個正整數,為所求三角形的數量。

Sample Input

樣例輸入1:1 1

樣例輸入2:2 2

Sample Output

樣例輸出1:4

樣例輸出2:76

先求出全集,再去掉三點共線的情況。

那如何列舉三點共線的情況呢?先將橫豎兩種情況算了,再考慮斜著的。列舉斜著的方法有很多,但比較可行的是先固定兩個點,再計算兩個點之間的點數。具體實現就是列舉兩點的相對位置,也可以理解為向量(a,b)

,然後就有(n+1-a)*(m+1-b)個位置可以當左上角的點。這兩點之間的點數就是gcd(a,b)-1。最後還要*2,對稱性嘛。

這麼列舉的好處是,兩點之間的相對位置去確定了之後,中間點的數量就確定了。

程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#define ll long long

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

ll n,m;
ll ans,tot;
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
int main() {
	n=Get(),m=Get();
	n++,m++;
	tot=n*m;
	ans=tot*(tot-1)/2*(tot-2)/3;
	if(n>=3) ans-=n*(n-1)/2*(n-2)/3*m;
	if(m>=3) ans-=m*(m-1)/2*(m-2)/3*n;
	for(int i=1;i<n;i++) {
		for(int j=1;j<m;j++) {
			ans-=(n-i)*(m-j)*(gcd(i,j)-1)*2;
		}
	}
	cout<<ans;
	return 0;
}