4558: [JLoi2016]方

4558: [JLoi2016]方

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

分析:

  容斥原理+各种神奇的计数。

  如果没有被删除了的点的话,直接计算就好了。

统计出所有的竖直放置的正方形,然后每个正方形里包含其边长个数正方形。

4558: [JLoi2016]方_第1张图片

设外边的正方形边长为a,公式就是$(n - a + 1) \times (m - a + 1) * a$,所以可以O(n)求出。

考虑减不合法的正方形。那么分为包含一个“坏点”,2个,3个,4个。

234的时候都可以直接枚举两个点,然后就可以确定出其他的两个点了,(这里可以确定两个点后,把所有枚举到的都加上,最后除以计算了多少次。三个的可以分别枚举三条边的时候都算上,所以除以3;4个点枚举边和对角线都算上了,所以除以6)。1个的最难算。

我们把一个点面向的四个方向分开计算,因为这是一个一样的过程。

可以知道,如果确定了一个点,和一个正方形(这个点在正方形的边上),那么就会确定唯一的一个以这个点为顶点的正方形

4558: [JLoi2016]方_第2张图片

那么我们只需要计算有多少个正方形的边上有这个点就行了,

4558: [JLoi2016]方_第3张图片

如果没有左边和右边的限制的话:

4558: [JLoi2016]方_第4张图片

如上图,左边和右边长度都为4,先不管是否超出边界,那么就是2,3,4...8,9,分别表示长度为1,2,3...7,8的正方形。就是$\frac{n \times (n+3)}{2}$。

加上边界h的限制,正方形的边长最大为h。加上左右边界的限制,正方形的最大边长为$min(l+r,h)$,设为$z$。

这样算出来,可能是有不合法的,左边界超出了,或者右边界超出了。

4558: [JLoi2016]方_第5张图片

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

还有一点,计算这些正方形的时候,超四个方向的和加起来,会重复计算一部分。

4558: [JLoi2016]方_第6张图片

最后根据容斥原理,算出来就行了。

代码:

 1 #include
 2 #include
 3 #include
 4 #include
 5 #include
 6 #include
 7 #include<set>
 8 #include
 9 #include
10 #include
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 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 }

 

转载于:https://www.cnblogs.com/mjtcn/p/10057705.html

你可能感兴趣的:(php)