bzoj 3505: [Cqoi2014]數三角形
阿新 • • 發佈:2017-08-14
之間 無法 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;code12 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
bzoj 3505: [Cqoi2014]數三角形