http://acm.hdu.edu.cn/showproblem.php?pid=1828
(矩形求周长类似矩形合并求面积,只是多了需要统计竖边的个数,对于每次求到的下底边长还要增加目前底边与下一条底边之间的竖边总长)
1,n个矩形有2*n条线段,按从小到大排列,如上图得到的线段顺序
2:
开始扫描第一条线段,得到总底边长sum为a这部分,得到竖边个数segnum=2,(每增加一条底边segnum+=2,每合并一条底边segnum-=2,无下底边比上底边多segnum=0)
于是得到一部分周长如图蓝色部分:
3:扫描第2条线段:
得到新的底边sum=a+b,则新增加的底边周长为sum-last,(last记录的是上一次的底边)
X表示该竖边被合并了segnum-=2,(由于底边合并了),所以segnum还是为2
则新增加周长为新增加的蓝色部分:
后面同理继续扫描
如果扫描到了上底边,则底边总长会相应的减少该上底边的长度,则abs(sum-last)就是增加上底边的长度
这里说下扫描周长时不像扫描面积一样只需扫描到第2n-1条线段就行
而是要扫描整个2n条线段,因为扫描底2n条线段时肯定是上底边,得到的下底边总长sum=0,但是abs(sum-last)就是增加这条上底边的长度
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<queue> #include<algorithm> #include<map> #include<cmath> #include<iomanip> #define INF 99999999 using namespace std; const int MAX=20000+10; int sum[MAX<<2];//统计下底边总长度 int segnum[MAX<<2];//统计没有被覆盖的竖边个数 bool lseg[MAX<<2],rseg[MAX<<2];//表示某个区间最左边和最右边是否有下底边多于上底边 int mark[MAX<<2];//表示下底边比上底边多的个数 struct seg{ int l,r,h,d; seg(){} seg(int x1,int x2,int H,int c):l(x1),r(x2),h(H),d(c){} bool operator <(const seg&a)const{ if(h == a.h)return d>a.d;//这里一定不能去掉,比如这组数据:2 0 0 4 4 0 4 4 8 return h<a.h; } }s[MAX]; void Upfather(int n,int left,int right){ if(mark[n]){ sum[n]=right-left+1; lseg[n]=rseg[n]=true; segnum[n]=2;//该区间被一条底边全部覆盖,可用竖边为2 } else if(left == right)sum[n]=lseg[n]=rseg[n]=segnum[n]=0; else{ sum[n]=sum[n<<1]+sum[n<<1|1]; segnum[n]=segnum[n<<1]+segnum[n<<1|1]; lseg[n]=lseg[n<<1]; rseg[n]=rseg[n<<1|1]; if(rseg[n<<1] && lseg[n<<1|1])segnum[n]-=2;//两个矩形相交(底边区域有重合)则减少两条竖边 } } void Update(int L,int R,int d,int n,int left,int right){ if(L<=left && right<=R){ mark[n]+=d; Upfather(n,left,right); return; } int mid=left+right>>1; if(L<=mid)Update(L,R,d,n<<1,left,mid); if(R>mid)Update(L,R,d,n<<1|1,mid+1,right); Upfather(n,left,right); } int main(){ int n,x1,x2,y1,y2; while(cin>>n){ int k=0,left=INF,right=-INF; for(int i=0;i<n;++i){ cin>>x1>>y1>>x2>>y2; s[k++]=seg(x1,x2,y1,1); s[k++]=seg(x1,x2,y2,-1); left=min(left,x1); right=max(right,x2); } sort(s,s+k); int ans=0,last=0; for(int i=0;i<k;++i){ if(s[i].l<s[i].r)Update(s[i].l,s[i].r-1,s[i].d,1,left,right); ans+=segnum[1]*(s[i+1].h-s[i].h);//竖边增加的总长 ans+=abs(sum[1]-last);//底边增加的总长 last=sum[1]; } printf("%d\n",ans); } }