poj 3695 && hdu 2461

96 篇文章 0 订阅

题目概述

有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: 7

Case 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值