bzoj 3505: [Cqoi2014]数三角形

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 970  Solved: 595
[Submit][Status][Discuss]

Description

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

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output

输出一个正整数,为所求三角形数量。

Sample Input

2 2

Sample Output

76

数据范围
1<=m,n<=1000
题解:
  原本以为是个水题,结果还是不会。。。首先,N*M的网格有(N+1)*(M+1)个点,所以不妨让N++,M++。如果不考虑共线的情况,从N*M个点中任取3个组成三角形,答案是C(N*M,3)。然后有一个useful的结论:在(x1,y1) (x2,y2)两点构成的线段(不含端点)上有gcd(x1-x2,y1-y2)-1个整点。所以固定了(x1,y1),(x2,y2)作为端点之后,第3个点的取值有gcd(x1-x2,y1-y2)-1种,以此来更新答案。但是枚举两个点的复杂度是无法接受的,由于点是按矩阵的形式排列的,所以可以用平移来解决,这样假定左端点在(1,1)只需枚举右端点就够了,注意左斜和右斜两种情况。
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<vector>
 9 using namespace std;
10 typedef long long LL;
11 LL N,M,tot,ANS,tmp;
12 inline LL C(LL n,LL m){
13     LL ans=1;
14     for(LL i=n-m+1;i<=n;i++) ans*=i;
15     for(LL i=1;i<=m;i++) ans/=i;
16     return ans; 
17 }
18 inline LL gcd(LL a,LL b){
19     if(b==0) return a;
20     else return gcd(b,a%b);
21 }
22 int main(){
23     scanf("%lld%lld",&N,&M); N++; M++;
24     if(M>N) swap(N,M);
25     tot=N*M;
26     ANS=C(tot,3);
27     ANS-=C(N,3)*M; 
28     ANS-=C(M,3)*N;
29     for(LL i=1;i<=N;i++){
30         for(LL j=1;j<=M;j++){
31             if(i!=1||j!=1){
32                 if(i==1||j==1) ANS-=(gcd(i,j)-1)*(N-i)*(M-j);
33                 else ANS-=2*(gcd(i,j)-1)*(N-i)*(M-j);
34             }
35         }
36     }
37     printf("%lld\n",ANS);
38     return 0;
39 } 

 


posted @ 2016-03-08 09:54  CXCXCXC  阅读(572)  评论(0编辑  收藏  举报