bzoj3505 / P3166 [CQOI2014]数三角形
前置知识:某两个点$(x_{1},,y_{1}),(x_{2},y_{2})\quad (x_{1}<x_{2},y_{1}<y_{2})$所连成的线段穿过整点的个数为$gcd(x_{2}-x_{1},y_{2}-y_{1})-1$
“注意三角形的三点不能共线。”
暗示你可以处理出总方案再减去三点共线的方案。
显然,总方案就是在$(n+1)*(m+1)$个点中任选$3$个。于是$tot=C((n+1)*(m+1),3)$
现在我们要算出三点共线的方案
对于直线上的三点共线,显然$tot1=n*C(m,3)+m*C(n,3)$
对于斜线上的三点共线,我们可以根据前置知识↑↑枚举。
然鹅暴力枚举复杂度是达到$O(n^{2}m^{2})$的
所以我们需要转化
注意到其实我们可以只枚举$l=x_{2}-x_{1},r=y_{2}-y_{1}$,相当于把这两个数据看做一个矩形的长和宽。
蓝后我们要算出整个大矩形中有几个这样的小矩形:$(n-l+1)*(m-r+1)$
每个矩形中包含$2$条对角线,所以$tot2*=2$
所以斜线上的三点共线$tot2=\sum_{i=1}^{n}\sum_{j=1}^{m}(gcd(i,j)-1)*(n-i+1)*(m-j+1)$
代码中为了方便事先把$n,m$都$+1$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define re register 5 using namespace std; 6 typedef long long ll; 7 ll m,n,ans; 8 int gcd(int a,int b){return b?gcd(b,a%b):a;} 9 int main(){ 10 scanf("%lld%lld",&n,&m);++n;++m; 11 ll tmp=n*m; 12 ans=tmp*(tmp-1)*(tmp-2)/6; 13 ans-=n*m*(m-1)*(m-2)/6; 14 ans-=m*n*(n-1)*(n-2)/6;//减去横向和纵向的三点共线 15 for(int i=1;i<n;++i) 16 for(int j=1;j<m;++j) 17 ans-=1ll*(gcd(i,j)-1)*(n-i)*(m-j)*2; 18 printf("%lld",ans); 19 return 0; 20 return 0; 21 }