题目大意:在一个以(0,0)为左下角,(w,h)为右上角的矩形内,有n个标记。统计有多少个小矩形,满足端点都在大矩形内的整点上,且平行于x轴和y轴,且这些矩形不包含任何一个被标记的点(包括边界上和端点)。这些矩形可以是退化的矩形(即面积为0)。答案mod 1 000 000 007。w,h <= 10^9,n <= 2000。
首先将所有的点进行离散化,那么对于每一个枚举出来的矩形(图中加粗矩形),如图,统计出有多少个小矩形的右上角在该矩形内。
为了避免重复,该矩形的范围是[x0, x1), [y0, y1)。我们的统计分成4个部分。
1.小矩形在该矩形内,这个可以用等差数列一类的东东算出来(要知道,该矩形内是没有标记点的。为什么,因为我们是离散化的);
2.小矩形左边的那一条边在该矩形左边(就是图中加粗矩形左边的灰色矩形), 右边的一条边在该矩形内。
3.小矩形下面的那一条边在该矩形下面(图中加粗矩形下面的灰侧矩形),上面的一条边也在该矩形内。
4.小矩形的左下角在合法区域内,右上角在该矩形内。所谓的合法区域,比如图中的橙色点,只要在橙色矩形内没有标记,那就是合法的点,所以橙色点是合法的,蓝色点是不合法的。这个合法点的个数统计,用一个栈来维护,因为这些点构成的形状类似楼梯的样子(图中有)。
统计的时候,用扫描线对x进行扫描,对于每一个x,再用一条扫描线对y进行扫描,这时候就用栈把信息传上去。
详细看代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
//....这个读入会快些,但这题没什么必要
inline int Scan() {
int res = 0;
char ch;
do {
ch = getchar();
}while (ch < '0' || ch > '9');
while (ch >= '0' && ch <= '9') {
res = res * 10 + (int) ch - '0';
ch = getchar();
}
return res;
}
#define maxn 2013
int n;
ll w, h;
#define x first
#define y second
// 坐标
typedef pair<ll, ll> Data;
// 标记的坐标, 分别按x,y排序,(指针类型)
Data dat[maxn], *rx[maxn], *ry[maxn];
//按x从小到大排序
inline bool cmpx(Data *i, Data *j) {
return i -> x < j -> x || (i -> x == j -> x && i -> y < j -> y);
}
//按y从小到大排序
inline bool cmpy(Data *i, Data *j) {
return i -> y < j -> y || (i -> y == j -> y && i -> x < j -> x);
}
void Init() {
w = (ll) Scan(), h = (ll) Scan(), n = Scan();
for (int i = 0; i < n; i ++) {
dat[i].x = (ll) Scan(), dat[i].y = (ll) Scan();
rx[i] = ry[i] = &dat[i];
}
dat[n].x = -1, dat[n].y = -1;
rx[n] = ry[n ++] = &dat[n]; //为了处理方便,多加个点
sort(rx, rx + n, cmpx);
sort(ry, ry + n, cmpy);
}
// 栈
Data *list[maxn];
// 统计点数
ll area[maxn];
const ll mod = 1000000007;
// 1 + 2 + 3 + ... + a 的和
inline ll C(ll a) {
return (((1 + a) * a) >> 1) % mod;
}
inline ll add(ll a, ll b) {
return (a + b) % mod;
}
inline ll mul(ll a, ll b) {
return (a * b) % mod;
}
void Solve() {
ll res = 0LL;
//再多加个点, 方便统计
rx[n] = ry[n] = &dat[n];
dat[n].x = w + 1, dat[n].y = h + 1;
//初始化栈底
list[0] = new Data;
list[0] -> x = list[0] -> y = -1;
area[0] = 0;
for (int i = 0; i < n; i ++)
// 对于x的扫描线
if (rx[i] -> x != rx[i+1] -> x) {
int x0 = rx[i] -> x, x1 = rx[i+1] -> x;
int j = 0, top = 0;
for (; j < n; j ++) {
// 对于y的扫描线
if (ry[j] -> x > x0) continue;
// 若纵坐标相等,则取横坐标最大的
while (ry[j] -> y == ry[j+1] -> y && ry[j+1] -> x <= x0) j ++;
// 找出下一条扫描线
int p = j + 1;
while (p < n && (ry[j] -> y == ry[p] -> y || ry[p] -> x > x0))
p ++;
int y0 = ry[j] -> y, y1;
if (p == n) y1 = h + 1;
else y1 = ry[p] -> y;
//统计1
res = add(res, mul(C(x1 - x0 - 1), C(y1 - y0 - 1)));
//统计2
res = add(res, mul(mul(x0 + 1, C(y1 - y0 - 1)), x1 - x0));
//统计3
res = add(res, mul(mul(y0 + 1, C(x1 - x0 - 1)), y1 - y0));
//把不合法的弹出
while (top > 0 && list[top] -> x < ry[j] -> x) top --;
//加入当前的点
list[++ top] = ry[j];
area[top] = add(area[top-1],
mul(y0 - list[top-1] -> y, x0 - list[top] -> x));
//统计4
res = add(res, mul(area[top],
mul(x1 - x0, y1 - y0)));
}
}
cout << res << endl;
}
int main() {
freopen("rectangles.in", "r", stdin);
freopen("rectangles.out", "w", stdout);
Init();
Solve();
return 0;
}