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;

}

你可能感兴趣的:(HDU)