1. 程式人生 > >Bzoj4558:分類討論 計算幾何 組合數學

Bzoj4558:分類討論 計算幾何 組合數學

del using 枚舉 ace i++ != rom mod lld

國際慣例的題面:

技術分享圖片

這題讓我爆肝啦......
這種計數顯然容斥,正好不含任何壞點的我們不會算,但是我們能算至少含零個壞點的,至少含一個壞點的,至少含兩個壞點的......
所以最終的答案就是(至少含零個壞點的-至少含一個壞點的+至少含兩個壞點的-至少含三個壞點的+至少含四個壞點的)。
然後就是怎麽計算的問題。
對於至少含零個壞點的,我們不妨設定所有點都是好點。
對於非正放的正方形,我們能找到一個正好包含它的最小的正放的正方形,顯然這樣的正方形是唯一的。

技術分享圖片
然後我們讓四個點在這個正方形的邊上滑動,顯然這四個點的每一組位置對應一個非正放的正方形(雖然正好在四個角上的是正放的)。
於是我們可以得出總方案數為sigma( i from 1 to min(n,m) ) i * ( n - i + 1 ) * ( m - i + 1 ) 。
這個東西可以O(n)計算。

對於正好有一個壞點的,我們考慮某個以某個個點P為角的正方形A,點P一定包含這個正方形A的最小正放正方形的角上或邊上。

技術分享圖片
於是我們枚舉這樣的正方形和點P能在的位置數量就好了。
對於點P的狀態,我們計算出它距離左邊界的距離l,右邊界距離r,上邊界距離h。

技術分享圖片
然後我們令t=min(l+r,h)。
如果我們不考慮有一些位置不能取到的話,答案應該為t*(t+3)/2。
然而這樣計算了一些不能取到的位置。當t>l時,我們多計算的位置數量為(t-l)*(t-l+1)/2。(手玩一下就明白了)
t>r時同理。這樣我們就能O(k)計算出至少含一個壞點的方案數。

對於正好含兩及以上個壞點的,我們枚舉兩個壞點,顯然一個正方形給你兩個點,他的位置就基本確定了。

技術分享圖片
我們可以分類討論三種情況,用向量計算出另外兩個點應該在的位置。註意某些情況下以這兩個點為對角線的正方形可能不在格點上。
然後對於含兩個的,我們直接計算可行的正方形數;對於含三個的,我們當另外兩個點有一個為壞點時計算;含四個的,我們當另外兩個點均為壞點時計算。
顯然含三個和含四個的我們算重了。所以應該分別除以C(3,2)和C(4,2)。

然後累加一下答案就好。
(然而計算垂直向量時沒有加負號讓我調了半天,老年選手身敗名裂)
代碼:

技術分享圖片
  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<tr1/unordered_set>
  4 using namespace std;
  5 using namespace tr1;
  6 typedef long long int lli;
  7 using namespace std;
  8 using namespace tr1;
  9 const int maxp=2e3+1e2;
 10 const int mod=1e8+7;
 11 
 12 struct Point {
13 int x,y; 14 friend bool operator < (const Point &a,const Point &b) { 15 return a.x != b.x ? a.x < b.x : a.y < b.y; 16 } 17 friend Point operator - (const Point &a,const Point &b) { 18 return (Point){a.x-b.x,a.y-b.y}; 19 } 20 friend Point operator + (const Point &a,const Point &b) { 21 return (Point){a.x+b.x,a.y+b.y}; 22 } 23 friend Point operator * (const Point &a,const int &b) { 24 return (Point){a.x*b,a.y*b}; 25 } 26 friend Point operator / (const Point &a,const int &b) { 27 return (Point){a.x/b,a.y/b}; 28 } 29 inline Point swp() const { 30 return (Point){y,-x}; 31 } 32 inline bool candiv() const { 33 return ( ! ( x & 1 ) ) && ( ! ( y & 1 ) ); 34 } 35 }pt[maxp]; 36 unordered_set<lli> st; 37 int n,m,t; 38 lli ans,ini,sig,dou,tri,qua; 39 40 inline void insert(const Point &p) { 41 lli h = (lli) p.x * ( m + 1 ) + p.y; 42 st.insert(h); 43 } 44 inline bool inside(const Point &p) { 45 return 0 <= p.x && p.x <= n && 0 <= p.y && p.y <= m; 46 } 47 inline bool legal(const Point &pa,const Point &pb) { 48 return inside(pa) && inside(pb); 49 } 50 inline bool have(const Point &p) { 51 lli h = (lli) p.x * ( m + 1 ) + p.y; 52 return st.find(h) != st.end(); 53 } 54 inline lli calcini(lli n,lli m) { 55 lli ret = 0 , lim = min( n , m ); 56 for(lli i=1;i<=lim;i++) ret = ( ret + ( n - i + 1 ) % mod * ( m - i + 1 ) % mod * i % mod ) % mod; 57 return ret; 58 } 59 inline lli calcedge(const lli &l,const lli &r,const lli &h) { 60 lli t = min( l + r , h ); 61 if( !t ) return 0; 62 lli ret = ( t * ( t + 3 ) >> 1 ) % mod; 63 if( t > l ) ret -= ( ( t - l ) * ( t - l + 1 ) >> 1 ) % mod; 64 if( t > r ) ret -= ( ( t - r ) * ( t - r + 1 ) >> 1 ) % mod; 65 return ( ret % mod + mod ) % mod; 66 } 67 inline lli calcsingle(lli x,lli y) { 68 const lli a = n - x , b = m - y , c = x , d = y; 69 lli ret = ( calcedge(d,b,a) + calcedge(d,b,c) + calcedge(a,c,b) + calcedge(a,c,d) ) % mod; 70 ret -= ( min(a,b) + min(b,c) + min(c,d) + min(d,a) ) % mod; 71 return ( ret % mod + mod ) % mod; 72 } 73 inline lli calcdouble(const Point &a,const Point &b) { 74 const Point delta = (a-b).swp(); 75 int ret = legal(a+delta,b+delta) + legal(a-delta,b-delta); 76 const Point mid = a + b , pa = mid + delta , pb = mid - delta; 77 if( pa.candiv() && pb.candiv() && legal(pa/2,pb/2) ) ++ret; 78 return ret; 79 } 80 inline lli calctriple(const Point &a,const Point &b) { 81 const Point delta = (a-b).swp(); 82 int ret = 0; 83 if( legal(a+delta,b+delta) ) ret += have(a+delta) + have(b+delta); 84 if( legal(a-delta,b-delta) ) ret += have(a-delta) + have(b-delta); 85 const Point mid = a + b , pa = mid + delta , pb = mid - delta; 86 if( pa.candiv() && pb.candiv() && legal(pa/2,pb/2) ) ret += have(pa/2) + have(pb/2); 87 return ret; 88 } 89 inline lli calcquad(const Point &a,const Point &b) { 90 const Point delta = (a-b).swp(); 91 int ret = 0; 92 if( legal(a+delta,b+delta) ) ret += ( have(a+delta) && have(b+delta) ); 93 if( legal(a-delta,b-delta) ) ret += ( have(a-delta) && have(b-delta) ); 94 const Point mid = a + b , pa = mid + delta , pb = mid - delta; 95 if( pa.candiv() && pb.candiv() && legal(pa/2,pb/2) ) ret += ( have(pa/2) && have(pb/2) ); 96 return ret; 97 } 98 99 int main() { 100 scanf("%d%d%d",&n,&m,&t); 101 for(int i=1;i<=t;i++) scanf("%d%d",&pt[i].x,&pt[i].y) , insert(pt[i]); 102 ini = calcini(n,m); 103 for(int i=1;i<=t;i++) sig += calcsingle(pt[i].x,pt[i].y); 104 for(int i=1;i<=t;i++) for(int j=i+1;j<=t;j++) { 105 dou += calcdouble(pt[i],pt[j]) , tri += calctriple(pt[i],pt[j]) , qua += calcquad(pt[i],pt[j]); 106 } 107 tri /= 3 , qua /= 6; 108 ans = ( ( ini - sig + dou - tri + qua ) % mod + mod ) % mod; 109 printf("%lld\n",ans); 110 return 0; 111 }
View Code

Bzoj4558:分類討論 計算幾何 組合數學