矩形计数

来源:互联网 发布:苹果广告屏蔽软件 编辑:程序博客网 时间:2024/05/18 06:21

题目大意:在一个以(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 2013int 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;}