hdu 1828 Picture(线段树 || 普通hash标记)

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

Picture

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2135    Accepted Submission(s): 1134

Problem Description
A number 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. An example with 7 rectangles is shown in Figure 1.
The corresponding boundary is the whole set of line segments drawn in Figure 2.
The vertices of all rectangles have integer coordinates.
 
Input
Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. 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.
0 <= number of rectangles < 5000 All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.
Please process to the end of file.
 
Output
Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.
 
Sample Input
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
 
Sample Output
228
 
Source
 
【题解1】:
普通哈市标记:
分别按x,y方向用以下结构体构造线段:
struct Line
{
    int s,t;   //存始末位置
    int x;
    int flag;  //标记是否是起始边 1  -1
}linex[N<<1],liney[N<<1];  //分别安x与y构造线段
int visit[N<<2];  //判断是否访问过
再分别按x,y方向遍历标记
 
如下图:
按x方向的遍历线段1,2,3,4
 
【代码1】:
  1 #include <iostream>
  2 #include <string.h>
  3 #include <stdio.h>
  4 #include <algorithm>
  5 #define N 5010
  6 using namespace std;
  7 
  8 struct Line
  9 {
 10     int s,t;   //存始末位置
 11     int x;
 12     int flag;  //标记是否是起始边 1  -1
 13 }linex[N<<1],liney[N<<1];  //分别安x与y构造线段
 14 
 15 int visit[N<<2];  //判断是否访问过
 16 
 17 bool cmp(Line a,Line b)  //sort排序用
 18 {
 19     return a.x<b.x;
 20 }
 21 
 22 int cal_x(int n)  //统计按x排序的平行于y的线段长
 23 {
 24     int i,j,cnt=0;
 25     for(i=1;i<=n;i++)
 26     {
 27         if(linex[i].flag == 1)  //如果是起始边
 28         {
 29             for(j=linex[i].s;j<linex[i].t;j++)
 30             {
 31                 if(visit[j]==0) cnt++;  //起始边,没访问过cnt++
 32                 visit[j]++;  //标记+1
 33             }
 34         }
 35         else  //如果是结束边
 36         {
 37             for(j=linex[i].s;j<linex[i].t;j++)
 38             {
 39                 if(visit[j]==1) cnt++;  //结束边,访问过cnt++
 40                 visit[j]--;  //标记-1,等待下一个起始边的进入
 41             }
 42         }
 43     }
 44     return cnt;
 45 }
 46 
 47 int cal_y(int n)  //同上,统计按y排序的平行于x的线段长
 48 {
 49     int i,j,cnt=0;
 50     for(i=1;i<=n;i++)
 51     {
 52         if(liney[i].flag == 1)
 53         {
 54             for(j=liney[i].s;j<liney[i].t;j++)
 55             {
 56                 if(visit[j]==0) cnt++;
 57                 visit[j]++;
 58             }
 59         }
 60         else
 61         {
 62             for(j=liney[i].s;j<liney[i].t;j++)
 63             {
 64                 if(visit[j]==1) cnt++;
 65                 visit[j]--;
 66             }
 67         }
 68     }
 69     return cnt;
 70 }
 71 
 72 int main()
 73 {
 74     int n;
 75     while(~scanf("%d",&n))
 76     {
 77         int i,cnt=0;
 78         for(i=0;i<n;i++)
 79         {
 80             int x1,y1,x2,y2;
 81             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
 82             x1+=N<<1;
 83             x2+=N<<1;
 84             y1+=N<<1;
 85             y2+=N<<1;   //平移N<<1,排除负数情况
 86 
 87             cnt++;
 88             linex[cnt].x = x1;
 89             linex[cnt].s = y1;
 90             linex[cnt].t = y2;
 91             linex[cnt].flag = 1;
 92 
 93             liney[cnt].x = y1;
 94             liney[cnt].s = x1;
 95             liney[cnt].t = x2;
 96             liney[cnt].flag = 1;
 97 
 98             cnt++;
 99             linex[cnt].x = x2;
100             linex[cnt].s = y1;
101             linex[cnt].t = y2;
102             linex[cnt].flag = -1;
103 
104             liney[cnt].x = y2;
105             liney[cnt].s = x1;
106             liney[cnt].t = x2;
107             liney[cnt].flag = -1;
108         }
109 
110         sort(linex+1,linex+1+cnt,cmp);  //线段按x进行排序
111         sort(liney+1,liney+1+cnt,cmp);  //线段按y进行排序
112 
113         int ans = 0;
114         memset(visit,0,sizeof(visit));
115         ans += cal_x(cnt);  //统计x
116         memset(visit,0,sizeof(visit));
117         ans += cal_y(cnt);  //统计y
118 
119         printf("%d\n",ans);
120     }
121     return 0;
122 }


【题解2】:线段树

线段树代码是参照别人的

【code】:

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

 

posted @ 2013-08-01 10:54  crazy_apple  阅读(251)  评论(0编辑  收藏  举报