线段树专辑—— hdu 1828 Picture

http://acm.hdu.edu.cn/showproblem.php?pid=1828

线段树求解重叠矩形周长。这个比求并面积交面积难度大一些,要多想想才能明白

回忆求并面积的时候的第二种方法,利用线段树树形dp出可以与下一个将要插入的线段去构成并面积的最大y坐标长度,即tree[1].len;

然后求并面积的时候,只需要将tree[i].len*(line[i+1].x-line[i].x)即可。

想一想,并面积求的是乘绩,很容易想到周长其实就是求和。也就是y坐标长度加上x坐标的长度。而tree[1].len就已经求的了目前y坐标的可加长度,只需要将它减去上一次得到的tree[1].len再取绝对值即可得到y坐标上的长度增加!  那x坐标的长度如何求呢?这里的方法是为线段树添加一个num域,该域值表示该区间是有几个子区间组成的,例如:

我们要插入左边矩形的红色那两根线,那么插入后,将得到右边这样的线段树,右边红色的数就是代表num值,[1,5]的那段num==2,说明它是由两个矩形的边组成的(左边那两根红色的),所以每次插入新线段后得到的x坐标的周长可以用 tree[1].num*(line[i+1].x-line[i].x)*2来得到。

这里只是说了个大概,具体的自己可以研究一下代码

View Code
  1 #include<iostream>
2 #include<string>
3 #include<algorithm>
4 #include<cmath>
5 using namespace std;
6
7 struct node
8 {
9 int l;
10 int r;
11 int len; //该区间可与下一个将要插入的线段组成并面积的长度
12 int cover; //该区间覆盖了几根线段
13 int num; //记录该区间由几个子区间组成了可与下一个将要插入的线段组成并面积,即可并子区间的数目
14 int r_cover; //记录该区间右节点是否被可并区间覆盖
15 int l_cover; //。。。。。坐。。。。。。。。。
16 };
17
18 node tree[100000];
19 int n;
20 int yy[10004],len;
21
22 struct Line
23 {
24 int l;
25 int r;
26 int x;
27 int cover;
28 };
29
30 Line line[10004];
31
32 int cmp(Line a,Line b)
33 {
34 return a.x<b.x;
35 }
36
37 int find(int x)
38 {
39 int l=0,r=len,mid;
40 while(l<=r)
41 {
42 mid=(l+r)/2;
43 if(yy[mid]==x)
44 return mid;
45 if(yy[mid]<x)
46 l=mid+1;
47 else
48 r=mid-1;
49 }
50 return l;
51 }
52
53 void build(int i,int l,int r)
54 {
55 tree[i].l=l;
56 tree[i].r=r;
57 tree[i].cover=tree[i].l_cover=tree[i].r_cover=tree[i].len=tree[i].num=0;
58 if(l+1==r)
59 return;
60 int mid=(l+r)/2;
61 build(2*i,l,mid);
62 build(2*i+1,mid,r);
63 }
64
65 void fun(int i)
66 {
67 if(tree[i].cover) //整个被覆盖
68 {
69 tree[i].len=yy[tree[i].r]-yy[tree[i].l]; //可用长度为整个区间
70 tree[i].l_cover=tree[i].r_cover=1; //左右节点都被覆盖了
71 tree[i].num=1; //由一个区间组成,即该区间
72 }
73 else if(tree[i].l+1==tree[i].r) //叶子区间
74 {
75 tree[i].len=0;
76 tree[i].l_cover=tree[i].r_cover=0;
77 tree[i].num=0;
78 }
79 else
80 {
81 tree[i].len=tree[2*i].len+tree[2*i+1].len; //dp
82 tree[i].l_cover=tree[2*i].l_cover; //左节点是否覆盖,取决于左子区间的左节点是否被覆盖
83 tree[i].r_cover=tree[2*i+1].r_cover; //同理
84 tree[i].num=tree[2*i].num+tree[2*i+1].num-tree[2*i].r_cover*tree[2*i+1].l_cover; //该线段可用长度的区间组成数等于左右子区间的组成数
85 } //但是,当左右子区间是连续的时候,结果要减一
86 }
87
88 void updata(int i,int l,int r,int w)
89 {
90 if(tree[i].l>r || tree[i].r<l)
91 return;
92 if(tree[i].l>=l && tree[i].r<=r)
93 {
94 tree[i].cover+=w;
95 fun(i);
96 return;
97 }
98 updata(2*i,l,r,w);
99 updata(2*i+1,l,r,w);
100 fun(i);
101 }
102
103 int main()
104 {
105 int i,x1,x2,y1,y2,m,a,b;
106 freopen("in.txt","r",stdin);
107 while(scanf("%d",&n)!=EOF)
108 {
109 m=0;
110 for(i=0;i<n;i++)
111 {
112 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
113 yy[m]=y1;
114 line[m].cover=1;
115 line[m].x=x1;
116 line[m].l=y1;
117 line[m++].r=y2;
118
119 yy[m]=y2;
120 line[m].cover=-1;
121 line[m].x=x2;
122 line[m].l=y1;
123 line[m++].r=y2;
124 }
125 sort(yy,yy+m);
126 sort(line,line+m,cmp);
127 len=1;
128 for(i=1;i<m;i++)
129 {
130 if(yy[i-1]!=yy[i])
131 yy[len++]=yy[i];
132 }
133 len--;
134 build(1,0,len);
135 int ans=0,pre=0;
136 for(i=0;i<m;i++)
137 {
138 a=find(line[i].l);
139 b=find(line[i].r);
140 updata(1,a,b,line[i].cover);
141 ans+=abs((tree[1].len-pre)); //加上y坐标上的周长
142 if(i==m-1)
143 break;
144 pre=tree[1].len;
145 ans+=tree[1].num*(line[i+1].x-line[i].x)*2;//加上x坐标上的周长,因为一个y边连着两个x边,所以乘二
146 }
147 printf("%d\n",ans);
148 }
149 return 0;
150 }



posted @ 2011-11-13 14:57  Accept  阅读(1116)  评论(0编辑  收藏  举报