题目大意:略
题目分析:要求矩形周长并。看到这题觉得和这几天写的矩形面积并差不多,然后就直接敲了个线段树+扫描线,写差不多了才发现和面积并还是有区别的。然后仿照求面积并的方法,在线段树节点里面加各种信息,结果总是不对。重新理一下思路发现,求周长并只需要考虑2种情况:水平的边和竖直的边,水平的边不难求,在每插入一条扫描线的时候结算一下,当前区间被分成了几段,用分成的段数*2*当前扫描线和前一条扫描线x方向上的差值就是该段水平方向上周长,要记录当前区间被分成几段的话,每个节点要加2个变量l,r分别表示当前区间是左连续还是右连续;竖直方向上的周长线也好求,如果插入的线段使当前区间覆盖的次数由1变为0,则此长度为右边界,如果使当前区间覆盖次数由0变为1,则此长度为左边界。照着这个思路写,需要记录当前区间未被覆盖的长度和恰好覆盖了一次的长度,不过这样写一直没写出来,果断重新写了一遍,重写的时候发现这样做太麻烦了,我们可以直接统计当前区间的总长度,同时记录上一个状态的时候区间总长度,那么这2次区间总长度之差的绝对值就是插入当前扫描线后总区间内新产生或消失的线段,即周长的左边界或右边界。这样一想就好写多了。不过还有一个问题要注意,如果2条扫描线的x相同,那么我们要先插入矩形左边界的扫描线,因为这2条线是重合的,先插入矩形右边界的扫描线的话,重合的边会算进去,但实际上是不能算的,这个可以看我代码后面最后一组测试数据,看了就明白了。
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5005;
struct node
{
int x,y1,y2,flag;
}line[N<<1];
struct nd
{
int len;
int cover;//当前区间覆盖数
int num;//当前区间中连续的子区间数
int l,r;//当前区间左右是否连续
}tree[N<<3];
int hash[N<<1];
int cmp(struct node a,struct node b)
{
if(a.x != b.x)
return a.x < b.x;
else
return a.flag > b.flag;//矩形的左边界先插
}
void build(int num,int s,int e)
{
tree[num].len = 0;
tree[num].cover = tree[num].l = tree[num].r = tree[num].num = 0;
if(s == e)
return;
int mid = (s + e)>>1;
build(num<<1,s,mid);
build(num<<1|1,mid + 1,e);
}
void pushup(int num,int s,int e)
{
if(tree[num].cover)
{
tree[num].num = 1;
tree[num].l = tree[num].r = 1;
tree[num].len = hash[e + 1] - hash[s];
}
else
{
tree[num].num = tree[num<<1].num + tree[num<<1|1].num;
if(tree[num<<1].r && tree[num<<1|1].l)
tree[num].num --;
tree[num].l = tree[num<<1].l;
tree[num].r = tree[num<<1|1].r;
tree[num].len = tree[num<<1].len + tree[num<<1|1].len;
}
}
void insert(int num,int s,int e,int l,int r,int add)
{
if(s == e)
{
tree[num].cover += add;
if(tree[num].cover == 0)
{
tree[num].num = 0;
tree[num].l = tree[num].r = 0;
tree[num].len = 0;
}
else
{
tree[num].num = 1;
tree[num].l = tree[num].r = 1;
tree[num].len = hash[e + 1] - hash[s];
}
return;
}
if(s == l && r == e)
{
tree[num].cover += add;
pushup(num,s,e);
return;
}
int mid = (s + e)>>1;
if(r <= mid)
insert(num<<1,s,mid,l,r,add);
else
{
if(l > mid)
insert(num<<1|1,mid + 1,e,l,r,add);
else
{
insert(num<<1,s,mid,l,mid,add);
insert(num<<1|1,mid + 1,e,mid + 1,r,add);
}
}
pushup(num,s,e);
}
int gety(int x,int len)
{
int l = 1;
int r = len;
int mid;
while(l <= r)
{
mid = (l + r)>>1;
if(hash[mid] == x)
return mid;
else
{
if(hash[mid] > x)
r = mid - 1;
else
l = mid + 1;
}
}
return -1;
}
int main()
{
int n;
int i,x1,x2,y1,y2;
int m;
while(scanf("%d",&n) != EOF)
{
for(i = 1;i < n + n;i += 2)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
line[i].x = x1;
line[i].y1 = y1;
line[i].y2 = y2;
line[i].flag = 1;
line[i + 1].x = x2;
line[i + 1].y1 = y1;
line[i + 1].y2 = y2;
line[i + 1].flag = -1;
hash[i] = y1;
hash[i + 1] = y2;
}
sort(hash + 1,hash + n + n + 1);
m = 2;
for(i = 2;i <= n + n;i ++)
if(hash[i] != hash[i - 1])
hash[m ++] = hash[i];
m --;
sort(line + 1,line + n + n + 1,cmp);
build(1,1,m);
int ans = 0;
int r,l;
int add = 0;
for(i = 1;i <= n + n;i ++)
{
int t = 0;
if(i > 1)
t = (tree[1].num * 2 * (line[i].x - line[i - 1].x));
ans += t;
l = gety(line[i].y1,m);
r = gety(line[i].y2,m) - 1;
insert(1,1,m,l,r,line[i].flag);
ans += abs(add - tree[1].len);
add = tree[1].len;
}
printf("%d\n",ans);
}
return 0;
}
//15MS 508K
/*
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
3
0 10 10 20
0 -5 10 5
0 -20 10 -10
2
0 0 15 10
5 5 10 15
2
0 0 5 5
5 0 10 5
*/
后记:这题本不难,但写了这么久,主要因为太固执了,调了好久没调出来就应该早点重新整理一下思路重写的。而且不能陷入思维定势,这题和矩形面积并是不一样的。