1. 程式人生 > >4558: [JLoi2016]方

4558: [JLoi2016]方

long 分開 枚舉 () ima inf {} main target

4558: [JLoi2016]方

https://lydsy.com/JudgeOnline/problem.php?id=4558

分析:

  容斥原理+各種神奇的計數。

  如果每個刪除點的話,直接計算就好了。

統計出所有的豎直放置的正方形,然後每個正方形裏包含其邊長個數正方形。

技術分享圖片

設外邊的正方形邊長為a,公式就是$(n - a + 1) \times (m - a + 1) * a$,所以可以O(n)求出。

考慮減不合法的正方形。那麽分為包含一個“壞點”,2個,3個,4個。

234的時候都可以直接枚舉兩個點,然後就可以確定出其他的兩個點了。1個的最難算。

我們把一個點面向的四個方向分開計算,因為這是一個一樣的過程。

可以知道,如果確定了一個點,和一個正方形(這個點在正方形的邊上),那麽就會確定唯一的一個以這個點為頂點的正方形

技術分享圖片

那麽我們只需要計算有多少個正方形的邊上有這個點就行了,

技術分享圖片

如果沒有左邊和右邊的限制的話:

技術分享圖片

加上邊界h的限制,正方形的邊長最大為h。加上左右邊界的限制,正方形的最大邊長為$min(l+r,h)$,設為$z$。

如果我們這樣算出來,顯然是有不合法的,左邊界超出了,或者右邊界超出了。

技術分享圖片

於是根據等差序列求和公式,可以直接算了。

還有一點,計算這些正方形的時候,超四個方向的和加起來,會重復計算一部分。

技術分享圖片

最後根據容斥原理,算出來就行了。

代碼:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<cctype>
 7 #include<set>
 8 #include<queue>
 9 #include<vector>
10 #include<map>
11 using
namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-0;return x*f; 17 } 18 19 const int N = 2005; 20 const int mod = 1e8 + 7; 21 22 struct Point{ 23 int x, y; 24 Point() {} 25 Point(int _x,int _y) { x = _x, y = _y; } 26 }A[N]; 27 int n, m; 28 LL ans, t1, t2, t3, t4; 29 set<LL> s; 30 31 LL Count1(int l,int r,int h) { 32 int z = min(l + r, h); 33 if (z == 0) return 0; 34 LL ans = 1ll * z * (z + 3) / 2; 35 if (z > l) ans -= 1ll * (z - l) * (z - l + 1) / 2; 36 if (z > r) ans -= 1ll * (z - r) * (z - r + 1) / 2; 37 return (ans + mod) % mod; 38 } 39 LL Calc1(int x,int y) { // 統計一個點可以作為多少個正方形的頂點。 40 int u = x, d = n - x, l = y, r = m - y; 41 LL ans = Count1(l, r, u) + Count1(l, r, d) + Count1(u, d, l) + Count1(u, d, r); ans %= mod; 42 ans = ans - min(u, l) - min(u, r) - min(d, l) - min(d, r); 43 return (ans + mod) % mod; 44 } 45 bool inmap(Point P) { 46 return (P.x >= 0 && P.x <= n && P.y >= 0 && P.y <= m); 47 } 48 void Calc234(Point P,Point Q) { 49 if (!inmap(P) || !inmap(Q)) return ; 50 int t = s.count(1ll * P.x * (m + 1) + P.y) + s.count(1ll * Q.x * (m + 1) + Q.y); 51 ++ t2; 52 if (t >= 1) t3 ++; 53 if (t >= 2) t3 ++, t4 ++; 54 } 55 int main() { 56 n = read(), m = read();int k = read(); 57 for (int i = 1; i <= k; ++i) { 58 A[i].x = read(), A[i].y = read(); 59 s.insert(1ll * A[i].x * (m + 1) + A[i].y); // 因為列是從0開始編號的,所以需要乘以(m+1),或者直接乘以2000000 60 } 61 for (int i = 1, lim = min(n, m); i <= lim; ++i) { 62 ans += (1ll * (n - i + 1) * (m - i + 1) % mod * i % mod); 63 if (ans >= mod) ans -= mod; 64 } 65 for (int i = 1; i <= k; ++i) { 66 t1 += Calc1(A[i].x, A[i].y); 67 if (t1 >= mod ) t1 -= mod; 68 } 69 for (int i = 1; i <= k; ++i) { 70 Point P = A[i]; 71 for (int j = i + 1; j <= k; ++j) { 72 Point Q = A[j]; 73 int dx = A[i].x - A[j].x, dy = A[i].y - A[j].y; 74 Calc234(Point(P.x + dy, P.y - dx), Point(Q.x + dy, Q.y - dx)); // 作為邊的情況 75 Calc234(Point(P.x - dy, P.y + dx), Point(Q.x - dy, Q.y + dx)); 76 if ((abs(dx) + abs(dy)) & 1) continue; 77 int dx2 = (dx - dy) / 2, dy2 = (dx + dy) / 2; 78 Calc234(Point(P.x - dx2, P.y - dy2), Point(Q.x + dx2, Q.y + dy2)); // 作為對角線的情況 79 } 80 } 81 ans = ans - t1 + t2 - t3 / 3 + t4 / 6; 82 ans = (ans + mod) % mod; 83 cout << ans; 84 return 0; 85 }

4558: [JLoi2016]方