HDU-2461 Rectangles 容斥定理,状态压缩

这题简单说就是求矩形的面积并,线段树?只有20个矩形,我们可以用容斥来做。但是这个有个比较麻烦的地方就是要求出任意组合情况下的面积并,试过几次每次进行求解的写法都一一超时了。这里选择在dfs的时候直接枚举题目将询问的状态,只要当前状态是其子集的话,就直接加到上面。最后M次询问就能够在O(1)的时间内完成了。296MS水过了。

 

代码如下:

 

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#define INF 10000
using namespace std;

int N, M, status[1250000], seq[100005];

struct Rec
{
    int x1, x2, y1, y2;
}e[50];

inline void Getint(int &t)
{
    char c;
    while (c = getchar(), c < '0' || c > '9') ;
    t = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') {
        t = t * 10 + c - '0';
    }
}

void dfs(int p, int x1, int y1, int x2, int y2, int sign, int sta)
{
    if (x1 >= x2 || y1 >= y2) return; // 如果合并区域为零
    if (p == N) {
        if (sta != 0) {
            for (int i = 1; i <= M; ++i) {
                if ((seq[i] | sta) <= seq[i]) { // 说明当前状态是i的子集
                    status[seq[i]] += sign * (x2 - x1) * (y2 - y1);
                }
            }
        }
        return;
    }
    dfs(p+1, x1, y1, x2, y2, sign, sta);
    dfs(p+1, max(x1, e[p+1].x1), max(y1, e[p+1].y1), min(x2, e[p+1].x2), min(y2, e[p+1].y2), -sign, sta|(1<<p));
}

int main()
{
    int R, c, sta, ca = 0;
    while (scanf("%d %d", &N, &M), N|M) {
        memset(status, 0, sizeof (status));
        for (int i = 1; i <= N; ++i) {
        //    scanf("%d %d %d %d", &e[i].x1, &e[i].y1, &e[i].x2, &e[i].y2);
            Getint(e[i].x1), Getint(e[i].y1), Getint(e[i].x2), Getint(e[i].y2);
        }
        printf("Case %d:\n", ++ca);
        for (int i = 1; i <= M; ++i) {
            sta = 0;
            Getint(R);
            for (int j = 1; j <= R; ++j) {
                Getint(c);
                sta |= 1 << (c-1);
            }
            seq[i] = sta;  // 将所有的状态都保留起来
        }
        dfs(0, 0, 0, INF, INF, -1, 0); // 一次dfs求出所有选择下的面积
        for (int i = 1; i <= M; ++i) {
            printf("Query %d: %d\n", i, status[seq[i]]);
        }
        puts("");
    }
    return 0;
}

 

下面是采用扫描线来解决这个问题,首先将所有要询问的矩形的x坐标全部保留起来,从小到大排序,去重,然后枚举每一个小的区域,对要询问的矩形进行区域的高度并,当高度被分割成两个区域时要马上计算出一部分的值,为了防止两块区域再次合并就要求对输出进来的矩形进行y轴的排序,保证下y轴(底边的y轴)是递增序的。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3fffffff
using namespace std;

int N, M, rec[25], line[50], Q;

struct Rectangle
{
    int x1, y1, x2, y2;
}e[25];

bool cmpy(int a, int b)
{
    return e[a].y1 < e[b].y1;
}

int Merge()
{
    int cnt = 0, L, R, U, D, sum = 0;
    for (int i = 1; i <= Q; ++i) {
        line[++cnt] = e[rec[i]].x1;
        line[++cnt] = e[rec[i]].x2;
    }
    sort(rec+1, rec+Q+1, cmpy); // 对rec存储的矩形进行y1排序,便于计算高度的并
    sort(line+1, line+1+cnt);
    cnt = unique(line+1, line+1+cnt) - (line + 1);
    for (int i = 2; i <= cnt; ++i) {
        L = line[i-1], R = line[i], U = 0, D = INF;
        for (int j = 1; j <= Q; ++j) {  // 遍历所有的矩形
            int c = rec[j];
            if (e[c].x1 <= L && e[c].x2 >= R) {
                if (e[c].y1 > U && U > D) {
                    sum += (R - L) * (U - D);
                    U = e[c].y2, D = e[c].y1;
                }
                else {
                    U = max(U, e[c].y2);
                    D = min(D, e[c].y1);
                }
            }
        }
        if (U > D) {
            sum += (R - L) * (U - D);
        }
    }
    return sum;
}

int main()
{
    int ca = 0;
    while (scanf("%d %d", &N, &M), N|M) {
        for (int i = 1; i <= N; ++i) {
            scanf("%d %d %d %d", &e[i].x1, &e[i].y1, &e[i].x2, &e[i].y2);
        }
        printf("Case %d:\n", ++ca);
        for (int i = 1; i <= M; ++i) {
            scanf("%d", &Q);
            for (int j = 1; j <= Q; ++j) {
                scanf("%d", &rec[j]);
            }
            printf("Query %d: %d\n", i, Merge());
        }
        puts("");
    }
    return 0;
}
posted @ 2012-07-28 20:26  沐阳  阅读(721)  评论(0编辑  收藏  举报