题目概述
有N个矩形,编号1到N,给定其左下角和右上角顶点坐标,其后进行M次查询,每次问其中R个矩形面积并
时限
2000ms/6000ms
输入
第一行两个正整数N,M,其后N行,每行四个整数,描述两个顶点坐标,其后M行,每行第一个数R,之后R个数为查询的矩形编号,输入到N=M=0结束
限制
1<=N<=20;1<=M<=1e5;0<=坐标值<=1000
输出
每组数据输出在M+1行中,第一行
Case #:
其中#为数据序数,从1开始,其后每行
Query #: @
其中#为询问序数,从1开始,@为所求面积并,每组输出后带一空行
样例输入
2 2
0 0 2 2
1 1 3 3
1 1
2 1 2
2 1
0 1 1 2
2 1 3 2
2 1 2
0 0
样例输出
Case 1:
Query 1: 4
Query 2: 7Case 2:
Query 1: 2
讨论
计算几何,扫描线+离散化+线段树,额知道这不是最优方案,但额实在缺乏智慧,想不到如何用容斥dfs一次解决,于是遵循通法而解之,如此一来,这个题又和poj 1151差不多了,只是算面积的时候需要先考虑一下是否问到了这个矩形,没问到的直接跳过即可,具体会在代码中注释
到底怎么写容斥,听说可以300ms拿下,谁来给额讲讲
题解状态
176K,1579MS,C++,2008B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 402
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-8
struct Ln//line 水平线的结构
{
int l, r, y, f, n;//left,right 水平线左右端点横坐标 y 纵坐标 f 下底边为1 上底边为-1 配合cover覆盖层用 num 所属矩形序号
bool operator<(const Ln &b)const
{
return y < b.y;
}
}lns[42];
int N, M;//矩形数 询问数
int hashx[42], L[MAXN], R[MAXN], c[MAXN], len[MAXN];//和以前一样 第一个是离散数组 后面四个是线段树的四个数组
bool mk[22];//marked 是否询问到了这个矩形
void build(int i, int l, int r)//建树 也是和以往一样 没有太多需要解释的
{
L[i] = l, R[i] = r;
if (l + 1 != r) {
build(i * 2, l, (l + r) / 2);
build(i * 2 + 1, (l + r) / 2, r);
}
}
void modify(int i, Ln &a)//更新树 也是毫无变动 完全一样
{
if (a.l == hashx[L[i]] && a.r == hashx[R[i]])
c[i] += a.f;
else if (a.l >= hashx[L[i * 2 + 1]])
modify(i * 2 + 1, a);
else if (a.r <= hashx[R[i * 2]])
modify(i * 2, a);
else {
Ln b = a;
b.r = hashx[R[i * 2]];
modify(i * 2, b);
b = a;
b.l = hashx[L[i * 2 + 1]];
modify(i * 2 + 1, b);
}
if (c[i])
len[i] = hashx[R[i]] - hashx[L[i]];
else
len[i] = len[i * 2] + len[i * 2 + 1];
}
void fun()
{
for (int p = 1; p <= N; p++) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);//input
lns[p].l = x1, lns[p].r = x2, lns[p].y = y1, lns[p].f = 1, lns[p].n = p, hashx[p] = x1;
lns[N + p].l = x1, lns[N + p].r = x2, lns[N + p].y = y2, lns[N + p].f = -1, lns[N + p].n = p, hashx[N + p] = x2;//将所有水平线放入数组lns 并初始化离散数组
}
sort(lns + 1, lns + 2 * N + 1);
sort(hashx + 1, hashx + 2 * N + 1);
build(1, 1, unique(hashx + 1, hashx + 2 * N + 1) - hashx - 1);//以去重后的离散数组中的值建树 这是从poj 1177后才有的思想
for (int p = 0; p < M; p++) {
memset0(mk);//先清零标记数组
int R, area = 0, lasty = 0;//R是询问中的矩形数 area 面积并 lasty 上一条有效水平线的纵坐标值 由于有些矩形被跳过 因而不能再简单的取下一条水平线的纵坐标做差作为为小矩形的高
scanf("%d", &R);//input
while (R--) {
int a;
scanf("%d", &a);//input
mk[a] = 1;//这个矩形被询问到了
}
bool f = 0;//flag 枚举第一条线时不能算面积 因为是第一条 还不构成图形
for (int i = 1; i <= 2 * N; i++)//枚举所有水平线
if (mk[lns[i].n]) {//这条线在某个被询问的矩形中
if (f)
area += len[1] * (lns[i].y - lasty);
modify(1, lns[i]);//先计算面积 然后用线更新 否则有效宽度len是下一次的 而用了上一次的纵坐标差 算出的面积不对
lasty = lns[i].y;//记录下此次的纵坐标
f = 1;
}
printf("Query %d: %d\n", p + 1, area);//output
}
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
int times = 0;
while (~scanf("%d%d", &N, &M) && (N || M)) {//input
printf("Case %d:\n", ++times);//output
fun();
printf("\n");//output
}
}
EOF