折腾快一天了都,磕磕绊绊地看看人家代码看看解释,自己画图输出中间过程,差不多都理解了, = = T T。。。
应该算是有两种做法。
1、直接离散化后先算平行于 Y 轴的矩形并后的线段,然后算X轴的。这个就是插入一条线段,删除一条线段,代码比较长。
2、陈宏WC99论文《数据结构的选择与算法效率——从IOI98试题PICTURE谈起》,太长了,没怎么看懂。不过从网上搜了些这题的题解,大致看懂了。
我就说下我对这种做法的理解,比较水。。。
这个题的线段树需要更新多个域。就我的代码来讲,Tnode(也就是我的线段树节点)中的:
1、l,r 跟往常线段树一样,是离散化后的下标。
2、len,当前节点的子树中覆盖的线的长度。
3、num,子树中被分为几段,这个主要是为了计算平行于X轴的线段长度。
4、cover,标记当前节点是否被覆盖。
5、rb lb 是标记当前节点的右端点和左端点是否被覆盖。找num的数量需要用到。
Sline结构体是扫描线,其中的flag标记是入边还是出边。
具体过程:前期处理过程和矩形面积并没啥区别(不会的话先去学那个,那个简单多了= =),关键是线段树上域的更新,更新num的时候,注意,如果两个线段是连一起的,那么需要减1(可以用rb 和lb判断),因为这个len是当前的总长度,所以求线段长度的时候,就需要当前总长度减去更新前的总长度(的绝对值),也就是这次扫描线后的长度。
嗯。。大致也就理解这么多了,线段树变形太多了,呜呜。。。任重而道远!!
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <stack> #include <climits> #define MID(x,y) ((x+y)>>1) #define L(x) ( x << 1 ) #define R(x) ( x << 1 | 1 ) #define BUG printf("here!!!\n"); using namespace std; const int MAX = 5010; struct Tnode{ int l,r,num,len,cover;bool lb,rb;}; struct Sline{ int x,y1,y2,flag;}; Tnode node[MAX<<2]; Sline l[MAX<<1]; int y[MAX<<1]; void add_line(int x1,int y1,int x2,int y2,int &cnt) { l[cnt].x = x1; l[cnt].y1 = y1; l[cnt].y2 = y2; l[cnt].flag = 1; y[cnt++] = y1; l[cnt].x = x2; l[cnt].y1 = y1; l[cnt].y2 = y2; l[cnt].flag = -1; y[cnt++] = y2; } void init() { memset(node,0,sizeof(node)); } void Build(int t,int l,int r) { node[t].l = l; node[t].r = r; node[t].num = 0; if( l == r - 1 ) return ; int mid = MID(l,r); Build(R(t),mid,r); Build(L(t),l,mid); } void Updata_len(int t) { if( node[t].cover > 0 ) { node[t].num = node[t].lb = node[t].rb = 1; node[t].len = y[node[t].r] - y[node[t].l]; } else if( node[t].l == node[t].r - 1 ) node[t].rb = node[t].lb = node[t].len = node[t].num = 0; else { node[t].rb = node[R(t)].rb; node[t].lb = node[L(t)].lb; node[t].len = node[L(t)].len + node[R(t)].len; node[t].num = node[L(t)].num + node[R(t)].num - node[R(t)].lb*node[L(t)].rb; } //两线段重合的话,得减一下。。 } void Updata(int t,Sline p) { if( y[node[t].l] >= p.y1 && y[node[t].r] <= p.y2 ) { node[t].cover += p.flag; Updata_len(t); return ; } if( node[t].l == node[t].r - 1 ) return ; int mid = MID(node[t].l ,node[t].r); if( p.y1 <= y[mid] ) Updata(L(t),p); if( p.y2 > y[mid] ) Updata(R(t),p); Updata_len(t); } int solve(int n,int cnt,Sline *l) { init(); Build(1,0,cnt-1); int ans = 0,last = 0,lines = 0; for(int i=0; i<n; i++) { Updata(1,l[i]); if( i >= 1 ) ans += 2 * lines * (l[i].x - l[i-1].x);//计算平行于x轴的长度 ans += abs(node[1].len - last); // 计算平行于y轴的长度 last = node[1].len; lines = node[1].num; } return ans; } bool cmp(Sline a,Sline b) { if( a.x == b.x ) return a.flag > b.flag; return a.x < b.x; } int main() { int n,x1,x2,y1,y2; while( ~scanf("%d",&n) ) { if( n == 0 ) { printf("0\n"); continue; } int cnt = 0; for(int i=0; i<n; i++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); add_line(x1,y1,x2,y2,cnt); } sort(y,y+cnt); sort(l,l+cnt,cmp); int t = cnt; t = unique(y,y+cnt) - y; int ans = solve(cnt,t,l); printf("%d\n",ans); } return 0; }