bzoj 3505: [Cqoi2014]数三角形
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
数据范围
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 }