USACO 5.5 Picture

Picture
IOI 1998

A number, N (1 <= N < 5000), of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter. Write a program to calculate the perimeter.

Figure 1 shows an example with seven rectangles: 

Figure 1. A set of seven rectangles

The corresponding boundary is the whole set of line segments drawn in Figure 2: 
 
Figure 2. The boundary of the set of rectangles

The vertices of all rectangles have integer coordinates. All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area. The numeric value of the result fits in a 32-bit signed representation.

PROGRAM NAME: picture

INPUT FORMAT

Line 1: N, the number of rectangles pasted on the wall.
Lines 2..N+1 In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate.

SAMPLE INPUT (file picture.in)

7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16

OUTPUT FORMAT

A single line with a non-negative integer which corresponds to the perimeter for the input rectangles.

SAMPLE OUTPUT (file picture.out)

228

———————————————————————————————————————————题解
这道题暴力完全可以做
 1 /*
 2 ID: ivorysi
 3 LANG: C++
 4 PROG: picture
 5 */
 6 #include <cmath>
 7 #include <iostream>
 8 #include <cstdio>
 9 #include <cstring>
10 #include <algorithm>
11 #define siji(i,x,y) for(int i=(x);i<=(y);++i)
12 #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
13 #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
14 #define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
15 #define inf 0x5f5f5f5f
16 #define ivorysi
17 #define mo 97797977
18 #define hash 974711
19 #define base 47
20 #define fi first
21 #define se second
22 #define pii pair<int,int>
23 #define esp 1e-8
24 typedef long long ll;
25 using namespace std;
26 struct node {
27     int s,t,y,on;
28     node (int st=0, int ed=0, int ceng=0, int num=0) {//要写上初值
29         s=st;t=ed;y=ceng;on=num;
30     }
31     bool operator <(const node &b) const {
32         return y<b.y || (y==b.y&&on>b.on);
33     }
34 }seg[10005];
35 int n;
36 int lx[5005],rx[5005],ly[5005],ry[5005],ans,level[20005];
37 void solve() {
38     scanf("%d",&n);
39     siji(i,1,n) {
40         scanf("%d%d%d%d",&lx[i],&ly[i],&rx[i],&ry[i]);
41     }
42     siji(i,1,n) {
43         seg[i*2-1] = node(lx[i],rx[i],ly[i],1);
44         seg[i*2] = node(lx[i],rx[i],ry[i],-1);
45     }
46     sort(seg+1,seg+2*n+1);
47     siji(i,1,2*n) {
48         xiaosiji(j,seg[i].s,seg[i].t) {
49             if(level[j+10000]+seg[i].on==1 && level[j+10000]==0) ++ans;
50             else if(level[j+10000]+seg[i].on==0 && level[j+10000]==1) ++ans;
51             level[j+10000]+=seg[i].on;
52         }
53     }
54     siji(i,1,n) {
55         seg[i*2-1] = node(ly[i],ry[i],lx[i],1);
56         seg[i*2] = node(ly[i],ry[i],rx[i],-1);
57     }
58     sort(seg+1,seg+2*n+1);
59     memset(level,0,sizeof(level));
60     siji(i,1,2*n) {
61         xiaosiji(j,seg[i].s,seg[i].t) {
62             if(level[j+10000]+seg[i].on==1 && level[j+10000]==0) ++ans;
63             else if(level[j+10000]+seg[i].on==0 && level[j+10000]==1) ++ans;
64             level[j+10000]+=seg[i].on;
65         }
66     }
67     printf("%d\n",ans);
68 }
69 int main(int argc, char const *argv[])
70 {
71 #ifdef ivorysi
72     freopen("picture.in","r",stdin);
73     freopen("picture.out","w",stdout);
74 #else
75     freopen("f1.in","r",stdin);
76 #endif
77     solve();
78     return 0;
79 }

哼,我可不是只是暴力可以做就暴力做的人

然后我用了许多种【奇奇怪怪而且错了的】线段树解法,让我深切的体会到我线段树学的是真不好

那么……

错解1

线段树区间加的模型,然后维护一个cover表示这个区间被覆盖了多少次,每次加上一个flag【如果这是矩形的底边flag=1,如果这是矩形的顶边flag=-1】

更新线段的时候,发现这个区间的cover=0,我们加上这个区间的长度*2

我们对于横向做一遍,对于纵向做一遍

……这,连样例都过不了

为什么呢,经过我gdb孜孜不倦的调试,我发现了原因……

如果这样的话我们需要加上5->9 *2 然而我们更新的话会直接刷掉0->9的cover

然后就有了

错解1.4

我们的区间可以是它左右两个区间的cover取个min嘛……

【然而这样有啥用?】

要不然就和自己的cover与左右两个区间的最小cover取个max……

【……啥】

错解1.47

我们的区间可以是它左右两个区间和嘛……

【你讲得出来道理吗就瞎搞】

错解2

我觉得1系列可能做不出来了

于是想到了2系列,我们记录一个区间被覆盖的长度,ans+最后根的长度的变化值【显得比较有道理了】

然而我是怎么写的……【自己都不忍心看】

维护一个len不断加上压进来的线段

【那样遇到连个重合的底边线段你难道不是随时就原地爆炸吗】

错解2.4

那我再维护一个cover,算这个线段有没有被覆盖,被覆盖就加上这个区间的长度

为啥是0……

哦还要加上左右两边的的区间长度

然后还是不对

错解2.47

怎么就不对呢……然后我翻了翻hzwer神犇矩形面积求并的代码

他好像没有维护lazy啊……

然后经过一番奇奇怪怪的思索

我好像有点理解我自己奇怪的算法为什么错了

【我这个代码不止维护了lazy还加了一句异常奇怪的cover=左右两边区间覆盖取个min】

维护lazy应该是没有什么问题的,但是这就相当于把一个大线段拆小,再复原

后面一句应该是因为这相当与把线段合并成一个大的,也没什么意义

两个一起上就有奇怪的化学反应?

所以正解3

压线段进树的正确姿势是维护一个cover记录压的层数,删除线段在对应的cover删除层数,然后记录一下这个区间总共的线段长度

最后维护到根就是我们想要的当前线段并

然后又有很多奇奇怪怪的问题

……例如数组越界导致的WA

  1 /*
  2 ID: ivorysi
  3 LANG: C++
  4 PROG: picture
  5 */
  6 #include <cmath>
  7 #include <iostream>
  8 #include <cstdio>
  9 #include <cstring>
 10 #include <algorithm>
 11 #define siji(i,x,y) for(int i=(x);i<=(y);++i)
 12 #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
 13 #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
 14 #define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
 15 #define inf 0x5f5f5f5f
 16 #define ivorysi
 17 #define mo 97797977
 18 #define hash 974711
 19 #define base 47
 20 #define fi first
 21 #define se second
 22 #define pii pair<int,int>
 23 #define esp 1e-8
 24 typedef long long ll;
 25 using namespace std;
 26 struct node {
 27     int s,t,y,on;
 28     node (int st=0, int ed=0, int ceng=0, int num=0) {//@要写上初值
 29         s=st;t=ed;y=ceng;on=num;
 30     }
 31     bool operator <(const node &b) const {
 32         return y<b.y || (y==b.y && on>b.on);
 33     }
 34 }seg[10005];
 35 struct data {
 36     int l,r,len,cover;
 37 }tree[100005];
 38 int n;
 39 int lx[5005],rx[5005],ly[5005],ry[5005],ans;
 40 void maketree(int u,int l,int r) {
 41     tree[u].l=l;tree[u].r=r;
 42     tree[u].len=tree[u].cover=0;
 43     if(l==r) {
 44         return;
 45     }
 46     int mid=(l+r)>>1;
 47     maketree(u<<1,l,mid);
 48     maketree(u<<1|1,mid+1,r);
 49 }
 50 void check(int u) {
 51     if(tree[u].cover>0) tree[u].len=tree[u].r-tree[u].l+1;
 52     else if(tree[u].l==tree[u].r) tree[u].len=0;
 53     //@少了这句话数组会越界从而导致……WA?开到200000可以解决问题
 54     else tree[u].len=tree[u<<1].len+tree[u<<1|1].len;
 55 }
 56 void change(int u,int x,int y,int c) {
 57     if(x<=tree[u].l && tree[u].r<=y) {
 58         tree[u].cover+=c;
 59         check(u);
 60         //@如果维护lazy:
 61         //@像区间加一样操作,可能覆盖到的地方没有办法及时减掉,因为矩形的边都是成对出现
 62         return;
 63     }
 64     int mid=(tree[u].l+tree[u].r)>>1;
 65     if(x<=mid) {change(u<<1,x,y,c);}
 66     if(y>mid) {change(u<<1|1,x,y,c);}
 67     check(u);
 68 }
 69 void solve() {
 70     scanf("%d",&n);
 71     siji(i,1,n) {
 72         scanf("%d%d%d%d",&lx[i],&ly[i],&rx[i],&ry[i]);
 73     }
 74     siji(i,1,n) {
 75         seg[i*2-1] = node(lx[i],rx[i],ly[i],1);
 76         seg[i*2] = node(lx[i],rx[i],ry[i],-1);
 77     }
 78     sort(seg+1,seg+2*n+1);
 79     maketree(1,-10000,10000);//@其实如果是浮点数就需要hash了,然而并不需要
 80     siji(i,1,2*n) {
 81         int temp=tree[1].len;
 82         change(1,seg[i].s,seg[i].t-1,seg[i].on);
 83         ans+=abs(temp-tree[1].len);
 84     }
 85     siji(i,1,n) {
 86         seg[i*2-1] = node(ly[i],ry[i],lx[i],1);
 87         seg[i*2] = node(ly[i],ry[i],rx[i],-1);
 88     }
 89     sort(seg+1,seg+2*n+1);
 90     maketree(1,-10000,10000);
 91     siji(i,1,2*n) {
 92         int temp=tree[1].len;
 93         change(1,seg[i].s,seg[i].t-1,seg[i].on);
 94         ans+=abs(temp-tree[1].len);
 95     }
 96     printf("%d\n",ans);
 97 }
 98 int main(int argc, char const *argv[])
 99 {
100 #ifdef ivorysi
101     freopen("picture.in","r",stdin);
102     freopen("picture.out","w",stdout);
103 #else
104     freopen("f1.in","r",stdin);
105 #endif
106     solve();
107     return 0;
108 }

 

 

 

 

 
posted @ 2017-02-23 11:23  sigongzi  阅读(468)  评论(0编辑  收藏  举报