bzoj3505 / P3166 [CQOI2014]数三角形

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 }
View Code

 

posted @ 2018-11-04 23:09  kafuuchino  阅读(154)  评论(0编辑  收藏  举报