1. 程式人生 > >bzoj3505 / P3166 [CQOI2014]數三角形

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