扫描线讲解,动态开点版线段树

扫描线

  首先,扫描线是干什么的?扫描线一般运用在图形上面,它和它的字面意思十分相似,就是一条线在整个图上扫来扫去,它一般被用来解决图形面积,周长等问题,以一道例题为例。给出n个正方形,这些正方形在平面直角坐标系中互相重叠摆放,但四条边都与坐标轴平行,例如下图所示。那么知道题目了,怎么运用呢?首先我们需要知道怎么用暴力解决这个问题,根据图片可知图中的面积是SABCD+SHEFG-SIDJE暴力搜索是个好东西,但是当数据范围大了怎么办?这里就要讲到扫描线。

  扫描线对于这道例题可以抽象为这四条紫色的直线(如上图l1,l2,l3,l4),仔细观察,可以看出这四条线把这个图形分割成三个矩形,那么我们就可以直接求这三个矩形再加和是不是就可以了?那么现在难点来了,怎样求这些矩形的面积。我们可以把题目中给的矩形的边转换成直线(如下图),即只留下这四条边,这四条线就是整个做法的核心。既然四条线已经看出来了,那么我们就可以一眼看出,面积就是从头到现在的扫描线的重影减去已经结束的长方形的边的投影承上两条扫描线的间距。再把这些乘积加在一起。

  下面就将如何实现了,首先我们可以想到用线段树求区间和来求这些投影的长度,那么区间如此之大(-1e8~1e8),怎么能建树呢?不会空间爆炸吗?所以就应该运用动态开点线段树,算一下每一个扫描线开一个节点,那么就是n个,一共有log21e8层所以是可以开的下的。根据这个说法,每一条边应该进行排序,由于扫描是从左到右,所以排序应该是把横坐标从小到大排序,所以每条边有三个属性:位置即横坐标,从那个点开始,从那个点结束,这两给点分别是纵坐标的两个端点。

 1 struct Line
 2 {
 3         int from,to,x,val;
 4 }line[2001];
 5 bool cmp1(const Line &a,const Line &b)
 6 {
 7         return a.x<=b.x;
 8 }
 9 int main()
10 {
11         scanf("%d",&n);
12         for(int i=1;i<=n;i++)
13         {
14                 scanf("%d%d%d%d",&a,&b,&c,&d);
15                 line[i*2-1].x=a;
16                 line[i*2-1].from=d+1;
17                 line[i*2-1].to=b;
18                 line[i*2-1].val=1;
19                 line[i*2].x=c;
20                 line[i*2].from=d+1;
21                 line[i*2].to=b;
22                 line[i*2].val=-1;
23         }
24         sort(line+1,line+1+2*n,cmp1);
25 }

扫描线排序

  下面讲解一下如何把线段树运用进去,我们把每一条边给定一个属性,这个矩形的左面边定义为入边,给一个+1的值,右边的边定义为出边,给一个-1的值,这就是边里的val的意义,那么这个和线段树又有什么关系呢?有了这个值我们可以快速地直接给线段树赋值,让其显示是否有边覆盖在上面,也就是下面代码中的cover数组的含义,如果cover数组有值不是零,那么这个区间就有边,即有r-l+1的贡献,否则则没有。这便是查询。在查询中一定要查到叶子节点,因为在扫描线中是没有上传值或是下传值的说法。

 1 int find(int l,int r,int p)
 2 {
 3         if(cover[p])
 4                 return sum[p];
 5         if(lp==rp&&rp==0)
 6                 return 0;
 7         int sum=0;
 8         if(lp!=0)
 9                 sum+=find(l,(l+r)>>1,lp);
10         if(rp!=0)
11                 sum+=find(((l+r)>>1)+1,r,rp);
12         return sum;
13 }

扫描线查询

  最难的要数修改,如果现在的节点被现在要加的边完全覆盖,那么直接修改就好啦,否则要递归的寻找他的儿子,如果没有儿子则动态开点出来,这边是修改的想法,当然在修改时不要忘记修改cover的值。

 1 void change(int l,int r,int x,int y,int &p,int delta)
 2 {
 3         if(!p)
 4                 p=++cnt;
 5         if(x<=l&&r<=y)
 6         {
 7                 cover[p]+=delta;
 8                 sum[p]=r-l+1;
 9                 return;
10         }
11         int mid=(l+r)>>1;
12         if(x<=mid)
13                 change(l,mid,x,y,lp,delta);
14         if(y>mid)
15                 change(mid+1,r,x,y,rp,delta);
16 }

扫描线修改

  大致就是这样,不会的可以评论发问题,我会解答。

原文地址:https://www.cnblogs.com/yangsongyi/p/8378629.html

时间: 2024-10-10 11:26:51

扫描线讲解,动态开点版线段树的相关文章

xdoj1023 IP查询 动态开点的线段树

xdoj1023 IP查询    动态开点的线段树 1023: IP查询 时间限制: 1 Sec  内存限制: 128 MB提交: 3473  解决: 228[提交][状态][讨论版] 题目描述 现实生活中,每一个IP段都指向一座城市.为了简化问题,我们将IP段直接看做一个整形数,每座城市也有自己的唯一标识ID,也可以看做一个整数.那么问题来了,现在已知有多个闭区间代表多个IP段,每个区间对应一个城市的ID.现在,小L要查询某个IP属于那个城市,希望聪明的你来帮他完成. 输入 第一行输入T,表示

xdoj1065 Efficent Allocation 动态开点的线段树

xdoj1065 Efficent Allocation  动态开点的线段树 1065: Efficent Allocation 时间限制: 8 Sec  内存限制: 256 MB提交: 24  解决: 3[提交][状态][讨论版] 题目描述 由于XDOJ评测机的一些奇怪行为,本题时限调整到8s. lx正在写一个内存分配器,支持下列操作: (1) malloc(size),分配一个大小为size字节的内存块,返回指向该内存块的指针. (2) free(p),释放起始地址是p的内存块. lx使用的

HDU 6681 Rikka with Cake(扫描线、动态开点线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=6681 题意 在矩形区域内有k条射线,问这些射线将矩形分成了多少区域 题解 容易发现答案为所有射线交点个数. 按y从排序扫描矩形区域,动态开点线段树维护区间内竖线的个数,由于n,m范围较大,需要离散化处理,但这样比较麻烦且此题空间足够所以建议用动态开点. 1 #define bug(x) cout<<#x<<" is "<<x<<endl 2 #defi

指针版线段树

只是作一下,以后必须得写数组版的...???(然而很好写? 哦对,唯一的好处就是内存少一点,没了.(coding量似乎并不会少很多?也不会多很多?雾) 还有很重要的一点就是慢...(尽管虽然没有慢多少?该卡还是卡?) 哎呀真是好纠结... 问了些神犇,似乎大家并不知道线段树还能用数组写... 呵呵... 然后看了一眼内存,指针严格开2n-1就好,而数组其实是要开4n的.... COJ上的数据太水了,数据只有大概... 所以呢.....要不我先用指针写几次再说? 不过是真心写着舒服. 1 #inc

zoj 2112 Dynamic Rankings 动态第k大 线段树套Treap

Dynamic Rankings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 Description The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query l

【BZOJ-1568】Blue Mary开公司 李超线段树 (标记可持久化)

1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 557  Solved: 192[Submit][Status][Discuss] Description Input 第一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益. 若单词为Project,则后

算法学习——动态图连通性(线段树分治+按秩合并并查集)

在考场上遇到了这个的板子题,,,所以来学习了一下线段树分治 + 带撤销的并查集. 题目大意是这样的:有m个时刻,每个时刻有一个加边or撤销一条边的操作,保证操作合法,没有重边自环,每次操作后输出当前图下所有联通块大小的乘积. 首先观察到如果没有撤销操作,那么直接用并查集就可以维护,每次合并的时候乘上要合并的两个并查集大小的逆元,然后乘上合并之后的大小即可. 那么来考虑撤销,观察到如果并查集不带路径压缩,应该是可以处理撤销操作的. 但我们并不能直接做,因为并查集的撤销必须按顺序来,就相当于每次合并

【BZOJ3939】Cow Hopscotch(动态开点线段树)

题意: 就像人类喜欢跳格子游戏一样,FJ的奶牛们发明了一种新的跳格子游戏.虽然这种接近一吨的笨拙的动物玩跳格子游戏几乎总是不愉快地结束,但是这并没有阻止奶牛们在每天下午参加跳格子游戏 游戏在一个R*C的网格上进行,每个格子有一个取值在1-k之间的整数标号,奶牛开始在左上角的格子,目的是通过若干次跳跃后到达右下角的格子,当且仅当格子A和格子B满足如下条件时能从格子A跳到格子B: 1.B格子在A格子的严格右方(B的列号严格大于A的列号) 2.B格子在A格子的严格下方(B的行号严格大于A的行号) 3.

HDU6183 Color it 动态开点线段树

网址:https://vjudge.net/problem/HDU-6183 题意: 给出以下操作:$“0”$代表清空所有颜色,$"1$ $x$ $y$ $c$$"$代表在坐标$(x,y)$涂上第$c$种颜色,$"2$ $x$ $y_1$ $y_2$$"$代表统计$x$轴上$[1,x]$和y轴上$[y_1,y_2]$的颜色数,一个点可以有多种颜色,$“3”$代表结束.数据保证$n,m \leq 1e6,0 \geq c \leq 50,y_1 \leq y_2$.