1. 程式人生 > >bzoj 3505: [Cqoi2014]數三角形

bzoj 3505: [Cqoi2014]數三角形

之間 無法 sum != 線段 amp logs con microsoft

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(不合法三角形個數)

技術分享
 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
code

bzoj 3505: [Cqoi2014]數三角形