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; }