poj1177(1)

/*********************************
poj 1177
下面的程序是按照《计算几何》上介绍的方法写的
这里主要说说seg结构体中的a, lbd, rbd的意义

用F表示矩形的并集
用L表示扫描时F的现时垂直线
对线段树中的某个节点segs[id]
segs[id].a 是L和(segs[id].l, segs[id].r)的不想交部分的两倍

segs[id].lbd = 1(如果segs[id].l是L和(segs[id].l, segd[id].r)中一个区间的下端)
             = 0 (如果segs[id].l不是L和(segs[id].l, segd[id].r)中一个区间的下端)

segs[id].rbd = 1(如果segs[id].r是L和(segs[id].l, segd[id].r)中一个区间的上端)
             = (如果segs[id].r不是L和(segs[id].l, segd[id].r)中一个区间的上端)

扫描的时候水平方向的线贡献的长度就是(lis[i].x - x0)*segs[0].a

可能会有点儿难以理解,看代码也许会更有帮助

受上一篇中这一句 if(lis[i].ly >= lis[i+1].hy || lis[i].hy <= lis[i+1].ly) break;
的启发,把这一句添加到下面的程序中就0msAC了!

*********************************/


#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <memory>
using namespace std;
const int N = 5005, M = 10000;
struct li{
int x, ly, hy;
void set(int x, int ly, int hy, bool is_l){
   this->x = x, this->ly = ly, this->hy = hy, this->is_l = is_l;
}
bool is_l;
}lis[N*2];

struct seg{
int l, r;
int c, m;
int a, lbd, rbd;
void set(int l, int r, int c, int m, int a, int lbd, int rbd){
   this->l = l, this->r = r, this->c = c;
   this->m = m, this->a = a, this->lbd = lbd, this->rbd = rbd;
}
}segs[N*8];
int n, cnt;
int y[N*2];
int hash[20005];

bool cmp(li l1, li l2){
return l1.x < l2.x;
}
void build(int id, int l, int r){
segs[id].set(l, r, 0, 0, 0, 0, 0);
if(l < r - 1){
   int mid = (l + r) >> 1;
   build(2*id+1, l, mid);
   build(2*id+2, mid, r);
}
}

void renew(int id){ //??
if(segs[id].c > 0){
   segs[id].a = 2, segs[id].lbd = segs[id].rbd = 1;
   segs[id].m = y[segs[id].r] - y[segs[id].l];
}
else if(segs[id].l == segs[id].r - 1){
   segs[id].m = 0;
   segs[id].a = 0;
   segs[id].lbd = segs[id].rbd = 0;
}
else{
   segs[id].m = segs[2*id+1].m + segs[2*id+2].m;
   segs[id].a = segs[2*id+1].a + segs[2*id+2].a - 2*segs[2*id+1].rbd*segs[2*id+2].lbd;
   segs[id].lbd = segs[2*id+1].lbd;
   segs[id].rbd = segs[2*id+2].rbd;
}
}

void insert(int id, int l, int r){
if(segs[id].l >= l && segs[id].r <= r){
   segs[id].c++;
   renew(id);
}else{
   if(segs[id].l < segs[id].r - 1){
    int mid = (segs[id].l + segs[id].r) >> 1;
    if(l < mid) insert(2*id+1, l, r);
    if(r > mid) insert(2*id+2, l, r);
    renew(id);
   }
}
}


void del(int id, int l, int r){
if(segs[id].l >= l && segs[id].r <= r){
   segs[id].c--;
   if(segs[id].c < 0) segs[id].c = 0;
   renew(id);
}else{
   if(segs[id].l < segs[id].r - 1){
    int mid = (segs[id].l + segs[id].r) >> 1;
    if(l < mid) del(2*id+1, l, r);
    if(r > mid) del(2*id+2, l, r);
    renew(id);
   }
}
}

 

 

int main(){
int i;
int x1, y1, x2, y2;
while(scanf("%d", &n) != EOF){
   for(i = 0; i < n; i++){
    scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    y1 += M; //使得所有的y都是正数
    y2 += M;
    lis[2*i+1].set(x1, y1, y2, true); //lis的计数从1开始
    lis[2*i+2].set(x2, y1, y2, false);
    y[2*i] = y1, y[2*i+1] = y2;
   }
   n <<= 1;
   sort(y, y + n);
   sort(lis+1, lis + 1 + n, cmp);
   cnt = unique(y, y + n) - y;
   for(i = 0; i < cnt; i++) hash[y[i]]=i;
   build(0, 0, cnt - 1);
   int m1,m0, ans, a;
   ans = m0 = 0;
   int x0 = lis[1].x;
   for(i = 1; i <= n; i++){ //横坐标相同的垂直线段要作类似的处理
    a = segs[0].a;
    while(i+1 <= n && lis[i].x == lis[i+1].x){
     //下面这一句真的很重要!!!
     if(lis[i].ly >= lis[i+1].hy || lis[i].hy <= lis[i+1].ly) break;
     if(lis[i].is_l) insert(0, hash[lis[i].ly], hash[lis[i].hy]);
     else del(0, hash[lis[i].ly], hash[lis[i].hy]);
     i++;
    }
    if(lis[i].is_l) insert(0, hash[lis[i].ly], hash[lis[i].hy]);
    else del(0, hash[lis[i].ly], hash[lis[i].hy]);
    ans += (abs(segs[0].m - m0) + (lis[i].x - x0)*a);
    m0 = segs[0].m;
    x0 = lis[i].x;
   }
   printf("%d/n", ans);
}
return 0;
}

 

 

 


/* 几组测试数据
2
0 0 5 5
5 0 10 5


2
0 0 5 5
6 0 11 5

2
0 0 5 5
0 5 5 10

3
0 0 5 5
0 4 5 9
0 8 5 13


2          
0 0 5 5
0 0 10 5


3  
0 0 5 5
0 0 10 5
0 0 15 5

3
0 0 5 5
5 0 10 5
10 0 15 5

2
0 0 5 5

0 6 5 11

3
0 0 5 5
0 6 5 11
0 12 5 17

3
0 0 5 5
0 5 5 10
0 10 5 15

 

2
0 0 5 5
5 0 10 5

2
0 0 5 5
5 5 10 10

2
0 0 5 5
6 0 11 5


47
-1105 -1155 -930 -285
-765 -1115 -615 -375
-705 -480 -165 -285
-705 -1200 -175 -1025
-275 -1105 -105 -385
-10 -1165 185 -285
315 -1160 400 -710
340 -1195 655 -1070
580 -1140 655 -265
325 -480 395 -335
365 -390 620 -265
365 -770 610 -665
815 -1195 1110 -1070
825 -760 1100 -660
810 -405 1115 -275
780 -700 860 -360
1065 -695 1130 -360
775 -1110 860 -735
1070 -1110 1145 -730
-1065 -95 140 260
-725 80 750 460
135 -135 490 840
135 -135 490 750
-520 40 -210 945
-595 620 215 695
670 -5 855 610
550 -75 830 -25
815 240 1085 370
980 -90 1125 145
280 150 490 315
-1035 -155 -845 -90
855 815 950 1030
785 980 860 1165
945 985 1015 1160
730 835 1075 895
875 695 935 790
-1165 420 -520 650
-1090 815 -210 945
-130 800 65 1160
120 980 690 1150
-1140 995 -125 1180
-825 1050 -195 1135
-90 865 10 1090
280 1045 625 1090
-655 1065 -245 1115
-1155 70 -790 315
-1005 110 -825 225

ans: 37000

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值