bzoj 3505: [Cqoi2014]数三角形
Description
给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。
注意三角形的三点不能共线。
Input
输入一行,包含两个空格分隔的正整数m和n。
Output
输出一个正整数,为所求三角形数量。
solution
给的 n,m 是格子数,实际上点数是n+1,m+1,所以我们先++n,++m 下面说的n,m是点数
我一开始是想着总方案数是 C(n*m,3),然后减去 竖列,横行,对角线 长度len的 C(len,3)
但是我只考虑斜率为 1/-1 的对角线,在wa了多次后看题解,发现竟然还有其他的对角线...
其实考虑到那种情况,我也做不出来,因为用 组合数 计算 三点共线的三角形 无法去重...
补集思想我想出来了
但是正解计算不合法三角形是:
枚举不合法三角形中 最左边和最右边的两个端点,计算中间端点的个数,即为当前端点为这样时的不合法三角形个数
计算中间端点有个定(gui)理(lv):
(x1,y1)和(x2,y2)之间的点数(不包括端点)为 gcd( abs(x1-x2),abs(y1-y2) )-1 个
暴力枚举两个端点是O(n^2*m^2*logn)肯定超时
优化是固定左端点为(1,1) (我习惯最上角是(1,1)) 枚举右端点,得到一条线段,然后将这条线段挪动
得到 (n-i+1)*(m-j+1) 个相同的线段 (这个不是定理,可以自己画画找规律)
O(n*m*logn)
最后 ans=C(n*m,3)-sum(不合法三角形个数)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstring> 2 #include<cstdio> 3 #include<iostream> 4 #define ll long long 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 using namespace std; 7 const int N=1066; 8 const int LEN=303; 9 ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);} 10 11 ll n,m; 12 13 int main(){ 14 15 scanf("%lld%lld",&n,&m); 16 if(n<m)swap(n,m); 17 ++n;++m; 18 19 ll ans=(ll)(n*m-1)*(ll)(n*m-2)*n*m/6; 20 ll temp; 21 for(int i=1;i<=n;++i) 22 for(int j=1;j<=m;++j) 23 { 24 if(i==1&&j==1) 25 temp=0; 26 else 27 if(i==1) 28 temp=j-2; 29 else 30 if(j==1) 31 temp=i-2; 32 else 33 temp=gcd(i-1,j-1)-1; 34 35 if(i!=1&&j!=1) 36 ans-=(temp*(n-i+1)*(m-j+1)*2); 37 else 38 ans-=(temp*(n-i+1)*(m-j+1)); 39 } 40 cout<<ans; 41 //while(1); 42 return 0; 43 } 44