线段树+扫描线(基本原理)
来源:互联网 发布:c语言入门自学书 编辑:程序博客网 时间:2024/06/06 02:20
这一部分是线段树的一个难点了,这写天做了这么多的这方面的题,一直是稀里糊涂的搞不太明白,但是又得理解,看到了一个别人转载的讲解,贴在这里以便回顾(侵删)。
扫描线法:
假想有一条扫描线,从左往右(从右往左),或者从下往上(从上往下)扫描过整个多边形(或者说畸形。。多个矩形叠加后的那个图形)。如果是竖直方向上扫描,则是离散化横坐标,如果是水平方向上扫描,则是离散化纵坐标。下面的分析都是离散化横坐标的,并且从下往上扫描的。
扫描之前还需要做一个工作,就是保存好所有矩形的上下边,并且按照它们所处的高度进行排序,另外如果是上边我们给他一个值-1,下边给他一个值1,我们用一个结构体来保存所有的上下边
struct segment
{
double l,r,h; //l,r表示这条上下边的左右坐标,h是这条边所处的高度
int f; //所赋的值,1或-1
}
接着扫描线从下往上扫描,每遇到一条上下边就停下来,将这条线段投影到总区间上(总区间就是整个多边形横跨的长度),这个投影对应的其实是个插入和删除线段操作。还记得给他们赋的值1或-1吗,下边是1,扫描到下边的话相当于往总区间插入一条线段,上边-1,扫描到上边相当于在总区间删除一条线段(如果说插入删除比较抽象,那么就直白说,扫描到下边,投影到总区间,对应的那一段的值都要增1,扫描到上边对应的那一段的值都要减1,如果总区间某一段的值为0,说明其实没有线段覆盖到它,为正数则有,那会不会为负数呢?是不可能的,可以自己思考一下)。
每扫描到一条上下边后并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到一块面积,并依此做下去,就能得到最后的面积
(这个过程其实一点都不难,只是看文字较难体会,建议纸上画图,一画即可明白,下面献上一图希望有帮助)
这里还有做的几道题。
二维线段树 (区间并) 查询被覆盖的面积 点击打开链接 简单题
模板!!!
代码如下:
#include<stdio.h>#include<string>#include<algorithm>#include<iostream>using namespace std;const int MAX=210;int N;double x[MAX<<2];struct Node{ double l, r; double h; int flag;}node[MAX<<2];int cmp(Node a, Node b){ return a.h<b.h;}struct Tree{ int l, r; int cnt; double len;}tree[MAX<<2];int findPos(int l, int r, double val){ int mid; while(l<=r) { mid=(l+r)>>1; if(x[mid]>val) r=mid-1; else if(x[mid]<val) l=mid+1; else break; } return mid;}void build(int rt, int left, int right){ tree[rt].l=left; tree[rt].r=right; tree[rt].len=0; tree[rt].cnt=0; if(left==right) return ; int mid=(left+right)>>1; build(rt<<1, left, mid); build(rt<<1|1, mid+1, right);}void pushUp(int rt){ if(tree[rt].cnt)//非0,整段覆盖 tree[rt].len=x[tree[rt].r+1]-x[tree[rt].l]; else if(tree[rt].l==tree[rt].r)//叶子 tree[rt].len=0; else//部分覆盖 tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;}void update(int rt, int left, int right, int val){ if(left<=tree[rt].l && tree[rt].r<=right)//全部包含 { tree[rt].cnt+=val; pushUp(rt); return ; } int mid=(tree[rt].l+tree[rt].r)>>1; if(left<=mid) update(rt<<1, left, right, val); if(right>mid) update(rt<<1|1, left, right, val); pushUp(rt);//计算该区间被覆盖的总长度}int main(){ int K=0; int l, r; double x1, x2, y1, y2; while(~scanf("%d", &N), N) { int cnt=0; for(int i=1; i<=N; i++) { scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); x[++cnt]=x1; node[cnt].l=x1; node[cnt].r=x2; node[cnt].h=y1; node[cnt].flag=1;//下边 x[++cnt]=x2; node[cnt].l=x1; node[cnt].r=x2; node[cnt].h=y2; node[cnt].flag=-1;//上边 } sort(x+1, x+cnt+1);//排序 sort(node+1, node+cnt+1, cmp); /* int M=1; for(int i=1; i<cnt; i++)//去重 可去可不去 if(x[i]!=x[i-1]) x[M++]=x[i]; */ build(1, 1, cnt); double ans=0; for(int i=1; i<=cnt; i++)//拿出每条横线并且更新 { l=findPos(1, cnt, node[i].l); r=findPos(1, cnt, node[i].r)-1; update(1, l, r, node[i].flag); ans+=tree[1].len*(node[i+1].h-node[i].h);//求面积 } printf("Test case #%d\nTotal explored area: %.2f\n\n", ++K, ans); } return 0;}
二维线段树 (区间交) 查询两次的面积 点击打开链接 难题
代码如下:
#include<iostream>#include<cstring>#include<cmath>#include<iomanip>#include<stdio.h>#include<algorithm>using namespace std;const int MAX=2010;int T, N, cnt;double x[MAX<<2];struct Node{ double l, r; double y; double flag;}node[MAX<<2];int cmp(Node a ,Node b){ return a.y<b.y;}struct Tree{ int l, r; double add; double len1, len2;}tree[MAX<<2];int findPos(double val)//找val在x[]中出现的位置{ int l=1, r=cnt; int mid; while(l<=r) { mid=(l+r)>>1; if(x[mid]<val) l=mid+1; else if(x[mid]>val) r=mid-1; else break; } return mid;}void build(int rt, int left, int right){ tree[rt].l=left; tree[rt].r=right; tree[rt].add=tree[rt].len1=tree[rt].len2=0;//全置0 if(left==right)//叶子 { return ; } int mid=(left+right)>>1; build(rt<<1, left, mid); build(rt<<1|1, mid+1, right);}void pushUp(int rt){ if(tree[rt].add>=2) { tree[rt].len1=tree[rt].len2=x[tree[rt].r+1]-x[tree[rt].l]; } else if(tree[rt].add==1) { tree[rt].len1=x[tree[rt].r+1]-x[tree[rt].l]; if(tree[rt].l==tree[rt].r) tree[rt].len2=0; else tree[rt].len2=tree[rt<<1].len1+tree[rt<<1|1].len1; } else { if(tree[rt].l==tree[rt].r) tree[rt].len1=tree[rt].len2=0; else { tree[rt].len1=tree[rt<<1].len1+tree[rt<<1|1].len1; tree[rt].len2=tree[rt<<1].len2+tree[rt<<1|1].len2; } }}void update(int rt, int left, int right, int val){ if(tree[rt].l==left && tree[rt].r==right) { tree[rt].add+=val; pushUp(rt); return ; } int mid=(tree[rt].l+tree[rt].r)>>1; if(right<=mid) update(rt<<1, left, right, val); else if(left>mid) update(rt<<1|1, left, right, val); else { update(rt<<1, left, mid, val); update(rt<<1|1, mid+1, right, val); } pushUp(rt);}int main(){ double x1, x2, y1, y2; int l, r; cin>>T; while(T--) { cnt=0; memset(node, 0, sizeof(node)); memset(tree, 0, sizeof(tree)); cin>>N; for(int i=1; i<=N; i++) { cin>>x1>>y1>>x2>>y2; x[++cnt]=x1; node[cnt].l=x1; node[cnt].r=x2; node[cnt].y=y1; node[cnt].flag=1;//左边 x[++cnt]=x2; node[cnt].l=x1; node[cnt].r=x2; node[cnt].y=y2; node[cnt].flag=-1;//右边 } //cout<<".......... cnt= "<<cnt<<endl; sort(x+1, x+cnt+1); build(1, 1, cnt); sort(node+1, node+cnt+1, cmp); double ans=0; l=findPos(node[1].l); r=findPos(node[1].r)-1; update(1, l, r, node[1].flag); for(int i=2; i<=2*N; i++) { ans+=tree[1].len2*(node[i].y-node[i-1].y); l=findPos(node[i].l); r=findPos(node[i].r)-1; update(1, l, r, node[i].flag); } printf("%.2lf\n", ans); //cout<<fixed<<setprecision(2)<<ans<<endl;//输出位数不准确 } return 0;}
二维线段树 求固定面积矩形(W*H)内星星和的最大值 点击打开链接
代码如下:
#include<iostream>#include<cstring>#include<algorithm>#include<stdio.h>using namespace std;const int MAX=20010;typedef long long LL;LL xis[MAX<<2];struct Node{ LL l, r; LL h; LL s; bool operator <(const Node & a) const { if(h==a.h) return s<a.s; return h<a.h; }}node[MAX];struct Tree{ int l, r; LL cnt; LL sum;}tree[MAX<<2];int findPos(int l, int r, LL val){ int mid; while(l<=r) { mid=(l+r)>>1; if(xis[mid]>val) r=mid-1; else if(xis[mid]<val) l=mid+1; else break; } return mid;}void pushUp(int rt){ tree[rt].sum=max(tree[rt<<1].sum, tree[rt<<1|1].sum);}void pushDown(int rt){ if(tree[rt].cnt) { tree[rt<<1].sum+=tree[rt].cnt; tree[rt<<1].cnt+=tree[rt].cnt; tree[rt<<1|1].sum+=tree[rt].cnt; tree[rt<<1|1].cnt+=tree[rt].cnt; tree[rt].cnt=0; }}void build(int rt, int left, int right){ tree[rt].l=left; tree[rt].r=right; tree[rt].sum=tree[rt].cnt=0; if(left==right) return ; int mid=(left+right)>>1; build(rt<<1, left, mid); build(rt<<1|1, mid+1, right);}void update(int L, int R, int c, int rt){ if(L<=tree[rt].l && tree[rt].r<=R) { tree[rt].cnt+=c; tree[rt].sum+=c; return ; } pushDown(rt); int mid=(tree[rt].l+tree[rt].r)>>1; if(L<=mid) update(L, R, c, rt<<1); if(mid<R) update(L, R, c, rt<<1|1); pushUp(rt);}int main(){ int N, W, H; while(cin>>N>>W>>H && N && W && H) { int m=0; LL ans=-1; LL x, y, c; int l, r; for(int i=1; i<=N; i++) { cin>>x>>y>>c; xis[++m]=x; node[m].l=x; node[m].r=x+W; node[m].h=y; node[m].s=c; xis[++m]=x+W; node[m].l=x; node[m].r=x+W; node[m].h=y+H; node[m].s=-c; } build(1, 1, m); sort(xis+1, xis+m+1); sort(node+1, node+m+1); for(int i=1; i<=m; i++) { l=findPos(1, m, node[i].l); r=findPos(1, m, node[i].r)-1; if(l<=r) update(l, r, node[i].s, 1); ans=max(ans, tree[1].sum); } cout<<ans<<endl; } return 0;}
- 线段树+扫描线(基本原理)
- 线段树[扫描线]
- 线段树+扫描线
- 线段树 扫描线
- 线段树扫描线
- 线段树-扫描线
- hdu1542 Atlantis (线段树+扫描线)
- 线段树扫描线(好)poj2482
- hdu5091(扫描线+线段树)
- poj1511 Atlantis(线段树+扫描线)
- HDU-1542(线段树+扫描线)
- hdu1542(线段树+扫描线)
- HDU1542(线段树+扫描线)
- poj 1151(线段树+扫描线)
- HDU1542 Atlantis(线段树+扫描线)
- HDU3255 Farming(线段树+扫描线)
- hdu1542 Atlantis (线段树|扫描线)
- poj 1177(线段树扫描线)
- HDU 5115 Dire Wolf (区间DP)
- springboot+mybatis+mysql(2)
- Mysql集群重启失败
- web.xml配置详解
- NOIP模拟:长跑路径(Dijkstra)
- 线段树+扫描线(基本原理)
- Vijos 1843 货车运输(最大生成树 + 树上倍增模板)
- zookeeper的参数配置详解
- 洛谷 1654 产品排序(sort) 背包DP+贪心
- 异常概念
- Spring,SpringMVC和hibernate整合小demo
- TypeLoadException: Could not load type 'System.IO.InternalBufferOverflowException' from解决方案
- 弱联HTTP
- 音视频,流媒体