hello kity
期待您的批阅,由于写解题报告时 不一定考虑的很周到,所以如果您有什么不懂的地方,请您留言,然而一天之内我肯定会看见您信息,再对代码注释详细,让您更好的阅读
分类
最新评论
最新留言
链接
RSS
计数器
223067
功能
HDU 1828(Picture)经典求矩形的并周长
spoiler
posted @ 2011年4月11日 14:41
in ACM经典题目之线段树
, 1992 阅读
题目大意:给出对角线让您求所有矩形构成整体的并周长。
解题分析:这里跟求面积所要求的基本操作是一致的。对Y进行离散化,然后建树,树节点里比起求并面积的多了几个域,一个是记录左右端点被覆盖的次数lb,rb,一个是记录区间内连续子段的个数。
通过上面的记录更新我们可以这样计算:
1)通过Y的长度就是,把这次在Y轴上的投影与上次的取差的绝对值,这就是它在Y上面的增量,把这些增量求和,这样就解决了Y上的周长问题!
2)因为线段树的节点里已经计算出目前该区间的连续子区间的个数,所以在这里就可以计算周长在X上的长度,因为Y上的段树就表明有该区间X的元线段要计算多少次,计算公式:sum+=2*(t[i].x-t[i-1].x)*lastn;在这里lastn表示上一次计算出的Y上投影的区间个数,也就是我这次计算所涉及的!
代码程序如下:
#include<iostream> #include<stdio.h> #include<algorithm> #include<math.h> using namespace std; int ym[10200];//注意区间要开大点 不然RE struct Tree{ int li,ri,lb,rb;//li,ri左右端点的值 int lenth,ans,cover;//lenth表示被覆盖至少一次的长度 //ans记录子区间个数,lb,rb记录左右端点被覆盖次数 //cover记录整个区间被覆盖的次数 }s[50000]; struct node{ int x,y1,y2; int flag; }t[50000];//表示左,右边的节点 int cmp1(int x,int y){ return x<y; } int cmp2(struct node st,struct node sd){ if(st.x!=sd.x) return st.x<sd.x; else return st.flag>sd.flag; //这里要非常注意,因为要计算并周长, //当两个边x一样的时候,要将左边先插入! } int len(int num){ if(s[num].cover>0) s[num].lenth=s[num].ri-s[num].li; else s[num].lenth=s[num+num].lenth+s[num+num+1].lenth; } int slen(int num){ if(s[num].cover>0) s[num].ans=1; else { s[num].ans=s[num+num].ans+s[num+num+1].ans; if(s[num+num].rb!=0&&s[num+num+1].lb!=0) s[num].ans--; //左子树的右端点如果跟右子树的左端点都被覆盖了 //说明在整个区间里,有一个区间横跨左右子树, //但被重复计算了,所以要减去1! } } int Build(int x,int y,int num){ s[num].li=ym[x]; s[num].ri=ym[y]; s[num].lenth=0; s[num].cover=s[num].rb=s[num].lb=s[num].ans=0; if(x+1==y) return 0; int mid=(x+y)>>1; Build(x,mid,num+num); Build(mid,y,num+num+1); } int modify(int num,struct node st){ if(st.y1==s[num].li) s[num].lb+=st.flag; if(st.y2==s[num].ri) s[num].rb+=st.flag; if(st.y1==s[num].li&&st.y2==s[num].ri){ s[num].cover+=st.flag; } else{ if(st.y1>=s[num+num].ri) modify(num+num+1,st); else if(st.y2<=s[num+num+1].li) modify(num+num,st); else{ struct node sd=st; sd.y2=s[num+num].ri; modify(num+num,sd); sd=st; sd.y1=s[num+num+1].li; modify(num+num+1,sd); } } len(num); slen(num); } int main(){ int cas,i,j; int x1,x2,y1,y2; int sum=0; while(scanf("%d",&cas)!=EOF){ sum=0; for(j=1,i=1;i<=cas;i++,j+=2){ scanf("%d %d %d %d",&x1,&y1,&x2,&y2); ym[j]=y1; t[j].x=x1; t[j].y1=y1; t[j].y2=y2; t[j].flag=1; ym[j+1]=y2; t[j+1].x=x2; t[j+1].y1=y1; t[j+1].y2=y2; t[j+1].flag=-1; } sort(ym+1,ym+j,cmp1); sort(t+1,t+j,cmp2); Build(1,j-1,1); int lastn=0,lasts=0; //lasts 表示上次Y上次的投影长度,lastn //表示Y上次的投影连续区间的个数 //其实连续区间就是指图形从上到下有多少个不相连的部分 //因为求周长,所以有多少个部分就要把X的区间长度 //乘以这个区间个数,因为他们每个部分的上下边不重叠 for(i=1;i<j;i++){ modify(1,t[i]); sum+=abs(lasts-s[1].lenth); if(i!=1){ sum+=lastn*(t[i].x-t[i-1].x)*2; } lastn=s[1].ans; //当初我答案一直错,检查了很久,最后终于发现是 //把lastn=s[1].ans;放在了if语句里面,这样的话影响了第2次的计算。所以周长计算出来会有点偏小,因为我第二次的lastn还是=0,应该是等于1,呵呵 lasts=s[1].lenth; } printf("%d\n",sum); } }