我是看这个博客看懂的https://www.cnblogs.com/whywhy/p/4214353.html;
扫描线就是线段树;
先看看最简单的应用(下面我觉得讲得不好,如果不想看直接看下面的注意或者模板吧)
计算图示图形的面积,我们可以上下扫描,也可以左右扫描,图示分成的三部分就是,左右扫描,把他每一个矩形的边都记录下来,并标记是左右边或者上下边关系。左右扫描的时候,线段树维护竖着的覆盖的长度,一个矩形的左边赋值为1,右边赋值为-1,大于1的时候表示这段线段肯定被覆盖了,如果等于0证明扫描到此处这个区间的有一对线段恰好是矩形的两边或者一部分,这样就可以确定一个区间的覆盖情况了。
从左到右的扫描线扫描的时候,线段树维护的是区间内被覆盖的总长度,然后乘以两条扫描线之间的距离,就是一块的面积,然后更新线段树,以此论推,得到的就是总面积了。
注意以下几点:
首先,离散化,由于一般坐标是浮点数,所以,要进行离散化,去重,最后对应回来,一般数据较大,不要用那个map离散化。
其次,就是我们维护的线段,离散化后我们以惯用的点思想去做会出错,比如覆盖区间1,4和6,10离散化后为1,2,3,4,这样更新维护下去可能会出错,因为我们这是线段比如n个点有n-1条线段。所以在更新的时候,我们把每一个数字当成一个点,但不过表示和理解时是他到下一个点的距离,所以每次计算区间长度的时候要记得吧右边加1。
最后,就是线段树的更新问题,覆盖区间并不是简单的覆盖问题,加一条线段就是加1,去一条线代就是减1,直到有的区间的值为0,表示没有覆盖。
关于区间覆盖这里有两道题,poj2528,由于数据比较水,不注意点与线段的转换也不会出错。第二道,hihocode1079这道题好像就水不过去了。
其实我觉得扫描线的模板都差不多,主要就是pushup的写法。
HDU 1542
题意不多说了就是矩形面积并,模板题;
///#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<functional>
#include<math.h>
#include<set>
#include<time.h>
#include<stack>
#include<string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int N = 210;
struct node
{
double l, r, h;
int f;///表示上下边或者左右边
}mar[N << 2];///存储边
bool cmp(node a, node b)
{
return a.h < b.h;
}
struct Tree
{
int l, r;
int cnt;///表示是否被覆盖
double len;///区间长度
int mid(){
return (l + r) >> 1;
}///线段树结点
}tree[N << 2];
int n; double x[N];
void build(int rt,int l,int r)
{
tree[rt].l = l;
tree[rt].r = r;
tree[rt].cnt = 0;
tree[rt].len = 0;
if (l == r)
return;
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void pushup(int rt)///其他都差不多,主要就是这个
{
if (tree[rt].cnt)///表示如果这个区间被覆盖
tree[rt].len = x[tree[rt].r + 1] - x[tree[rt].l];///直接相减
else if (tree[rt].l == tree[rt].r)
tree[rt].len = 0;///没有覆盖而且还是叶子结点当然是0了
else
tree[rt].len = tree[rt << 1].len + tree[rt << 1 | 1].len;///线段树经常出现的操作
}
void update(int rt, int l, int r, int val)
{
if (l <= tree[rt].l&&tree[rt].r <= r)
{
tree[rt].cnt += val;
pushup(rt);///向上更新
return;
}
int mid = tree[rt].mid();
if (l <= mid)
update(rt << 1, l, r, val);
if (r > mid)
update(rt << 1 | 1, l, r, val);
pushup(rt);
}
int findp(int l, int r, double key)
{
int mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (x[mid] > key)
r = mid - 1;
else if (x[mid] < key)
l = mid + 1;
else
break;
}
return mid;
}
int main()
{
int kase = 0, l, r;
double x1, y1, x2, 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;
mar[cnt].l = x1;
mar[cnt].r = x2;
mar[cnt].f = 1;
mar[cnt].h = y1;
x[++cnt] = x2;
mar[cnt].l = x1;
mar[cnt].r = x2;
mar[cnt].f = -1;
mar[cnt].h = y2;///存储边
}
sort(x + 1, x + cnt + 1);///离散化
sort(mar + 1, mar + cnt + 1, cmp);///把线代排序,好计算面积
build(1, 1, cnt);
double ans = 0;
for (int i = 1; i <= cnt; i++)
{
l = findp(1, cnt, mar[i].l);///这儿也可以用lower_bound()
r = findp(1, cnt, mar[i].r) - 1;///也是点与线的转换
update(1, l, r, mar[i].f);
ans += tree[1].len*(mar[i + 1].h - mar[i].h);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++kase, ans);
}
return 0;
}
HDU 4419
给出R,G,B三种颜色的矩形的数据,求最后7种颜色(R,G,B,RG,RB,GB,RGB)的面积是多少?
这道题就很难了,最简单最暴力的做法就是7颗线段树去维护7种颜色覆盖的情况。
好了说一下正常的做法,就是一颗线段树,维护7种颜色,想必大家都知道简单的状态压缩吧,就是用二进制的01表示颜色的有无,再加以判断,本题做法就是用一个cnt[4]表示三种基本颜色的覆盖情况,然后维护sta表示最终颜色,说着简单,但不过还是很难的,本蒟蒻也是乱写了几发,最后看了别人写的才弄出来的,其他都是套路。具体看代码
///#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<functional>
#include<math.h>
#include<set>
#include<time.h>
#include<stack>
#include<string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=20010;
struct node
{
int l,r,h;
int f,col;
}mar[N<<1];
int x[N<<1],n;
bool cmp(node a,node b)
{
return a.h<b.h;
}
struct Tree
{
int l,r;
int cnt[4];
int len[10];
int mid(){
return (l+r)>>1;
}
}tree[N<<2];
void build(int rt,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
for(int i=1;i<=7;i++)
tree[rt].len[i]= 0;
for(int i=0;i<=2;i++)
tree[rt].cnt[i]= 0;
if(l==r)
return;
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void pushup(int rt)
{
int sta=0;
for(int i=0;i<=2;i++)
{
if(tree[rt].cnt[i]>0)
sta+=(1<<i);///开始合成颜色
}
if(sta)
{
memset(tree[rt].len,0,sizeof(tree[rt].len));
tree[rt].len[sta]=x[tree[rt].r+1]-x[tree[rt].l];
if(tree[rt].l==tree[rt].r)
return;
for(int i=1;i<=7;i++)
{
if(sta!=(sta|i))
{
int tmp=tree[rt<<1].len[i]+tree[rt<<1|1].len[i];
tree[rt].len[sta]-=tmp;
tree[rt].len[sta|i]+=tmp;
}///开始更新,自己画个图看看
}
}
else if(tree[rt].l==tree[rt].r)
memset(tree[rt].len,0,sizeof(tree[rt].len));
else
{
for(int i=1;i<=7;i++)
{
tree[rt].len[i]=tree[rt<<1].len[i]+tree[rt<<1|1].len[i];
}
}
return ;///其实总的结构还是差不多的
}
void update(int l,int r,int rt,int val,int col)
{
if(l<=tree[rt].l&&tree[rt].r<=r)
{
tree[rt].cnt[col]+=val;
pushup(rt);
return;
}
int mid=tree[rt].mid();
if(l<=mid)
update(l,r,rt<<1,val,col);
if(r>mid)
update(l,r,rt<<1|1,val,col);
pushup(rt);
}
int findp(int l,int r,int val)
{
int mid,low=l,high=r;
while(low<=high)
{
mid=(low+high)>>1;
if(x[mid]>val)
high=mid-1;
else if(x[mid]<val)
low=mid+1;
else break;
}
return mid;
}
ll ans[10];
int main()
{
int T,kase=0;scanf("%d",&T);
char ch[3];int x1,y1,x2,y2;
while(T--)
{
int cnt=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s%d%d%d%d",ch,&x1,&y1,&x2,&y2);
int tmp;
if(ch[0]=='R')
tmp=0;
else if(ch[0]=='G')
tmp=1;
else
tmp=2;
x[++cnt]=x1;
mar[cnt].l=x1;
mar[cnt].r=x2;
mar[cnt].h=y1;
mar[cnt].f=1;
mar[cnt].col=tmp;
x[++cnt]=x2;
mar[cnt].l=x1;
mar[cnt].r=x2;
mar[cnt].h=y2;
mar[cnt].f=-1;
mar[cnt].col=tmp;
}
sort(x+1,x+cnt+1);
sort(mar+1,mar+cnt+1,cmp);
int M=1;
for(int i=2;i<=cnt;i++)
{
if(x[i]!=x[i-1])
x[++M]=x[i];
}
build(1,1,M);
memset(ans,0,sizeof(ans));
for(int i=1;i<=cnt;i++)
{
int l=findp(1,M,mar[i].l);
int r=findp(1,M,mar[i].r)-1;
update(l,r,1,mar[i].f,mar[i].col);
for(int j=1;j<=7;j++)
{
ans[j]+=(ll)tree[1].len[j]*(mar[i+1].h-mar[i].h);
}
}
printf("Case %d:\n",++kase);
swap(ans[3],ans[4]);
for(int i = 1; i <= 7; i++)
printf("%lld\n",ans[i]);
}
return 0;
}
HDU 1255
这道题就比上一道题简单多了,只求矩形相交的面积,也是用两个来记录覆盖1次和覆盖多次的长度,次数,其他都是套路,就是pushup不一样,这道题可能要加上一个eps ,我也不懂为什么,我看人家vs用lf就没有问题,下次用C++试一试。
///#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<functional>
#include<math.h>
#include<set>
#include<time.h>
#include<stack>
#include<string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int N = 2010;
#define eps 1e-8
int cmpd(double x)
{
if(fabs(x)<=eps) return 0;
if(x>0) return 1;
return -1;
}
struct node
{
double l,r,h;
int f;
}mar[N];
bool cmp(node a,node b)
{
return cmpd(a.h-b.h)<0;
}
int n;double x[N];
struct Tree
{
int l,r;
int cnt;
double len1,len2;
int mid(){
return (l+r)>>1;
}
}tree[N<<2];
void build(int rt,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
tree[rt].len1=0;
tree[rt].len2=0;
tree[rt].cnt=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void pushup(int rt)
{
if(tree[rt].cnt>1)
{
tree[rt].len2=tree[rt].len1=x[tree[rt].r+1]-x[tree[rt].l];
}
else if(tree[rt].cnt==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 l,int r,int val)
{
if(l<=tree[rt].l&&tree[rt].r<=r)
{
tree[rt].cnt+=val;
pushup(rt);
return;
}
int mid=tree[rt].mid();
if(l<=mid)
update(rt<<1,l,r,val);
if(r>mid)
update(rt<<1|1,l,r,val);
pushup(rt);
}
int findp(int l,int r,double val)
{
int mid;
while(l<=r)
{
mid=(l+r)>>1;
if(cmpd(x[mid]-val)>0)
r=mid-1;
else if(cmpd(x[mid]-val)<0)
l=mid+1;
else break;
}
return mid;
}
int main()
{
int T;scanf("%d",&T);
double x1,y1,x2,y2;
while(T--)
{
int cnt=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
x[++cnt]=x1;
mar[cnt].l=x1;
mar[cnt].r=x2;
mar[cnt].h=y1;
mar[cnt].f=1;
x[++cnt]=x2;
mar[cnt].l=x1;
mar[cnt].r=x2;
mar[cnt].h=y2;
mar[cnt].f=-1;
}
sort(x+1,x+cnt+1);
sort(mar+1,mar+cnt+1,cmp);
build(1,1,cnt);
double ans=0;
int l,r;
for(int i=1;i<=cnt;i++)
{
l=findp(1,cnt,mar[i].l);
r=findp(1,cnt,mar[i].r)-1;
update(1,l,r,mar[i].f);
ans+=tree[1].len2*(mar[i+1].h-mar[i].h);
}
printf("%.2f\n",ans+eps);
}
return 0;
}
HDU 1828
这个就是扫描线的宁一个应用了,求合成图像的周长,首先我说我用的简单做法,首先左右方向的周长怎么算呢,就是依次按照扫描顺序更新,每次加上 上一次的覆盖的总长度 减 这一次的覆盖的总长度,可以自己画个图看看是不是的,就是左右方向的周长,上下方向呢,就是在上下方向在扫描一次呗。还有高级的做法好像扫描一次就可以了,但不过还有维护,另一个方向的线段数目,我反正不会啦;
///#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<functional>
#include<math.h>
#include<set>
#include<time.h>
#include<stack>
#include<string>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=10010;
struct node
{
int l,r,h,f;
}mar[N<<1];
node lar[N<<1];
int x[N<<1],n,xx[N<<1];
bool cmp(node a,node b)
{
if(a.h==b.h)
return a.f>b.f;
return a.h<b.h;
}
struct Tree
{
int l,r;
int cnt,len;
int mid(){
return (l+r)>>1;
}
}tree[N<<2];
void build(int rt,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
tree[rt].cnt=tree[rt].len=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void pushup(int rt)
{
if(tree[rt].cnt)
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 l,int r,int val)
{
if(l<=tree[rt].l&&tree[rt].r<=r)
{
tree[rt].cnt+=val;
pushup(rt);
return;
}
int mid=tree[rt].mid();
if(l<=mid)
update(rt<<1,l,r,val);
if(r>mid)
update(rt<<1|1,l,r,val);
pushup(rt);
}
int findp(int l,int r,int 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;
}
int main()
{
int x1,y1,x2,y2;
while(~scanf("%d",&n))
{
int cnt=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x[++cnt]=x1,xx[cnt]=y1;
mar[cnt].l=x1,lar[cnt].l=y1;
mar[cnt].r=x2,lar[cnt].r=y2;
mar[cnt].h=y1,lar[cnt].h=x1;
mar[cnt].f=1,lar[cnt].f=1;
x[++cnt]=x2,xx[cnt]=y2;
mar[cnt].l=x1,lar[cnt].l=y1;
mar[cnt].r=x2,lar[cnt].r=y2;
mar[cnt].h=y2,lar[cnt].h=x2;
mar[cnt].f=-1,lar[cnt].f=-1;
}
sort(x+1,x+cnt+1);
sort(mar+1,mar+cnt+1,cmp);
int M=1,N=1;
for(int i=2;i<=cnt;i++)
if(x[i]!=x[i-1])
x[++M]=x[i];
build(1,1,M);
int ans=0,last=0;
for(int i=1;i<cnt;i++)
{
int l=findp(1,M,mar[i].l);
int r=findp(1,M,mar[i].r)-1;
update(1,l,r,mar[i].f);
ans+=abs(last-tree[1].len);
last=tree[1].len;
}
ans+=last;
sort(xx+1,xx+cnt+1);
sort(lar+1,lar+cnt+1,cmp);
M=1;
for(int i=2;i<=cnt;i++)
if(xx[i]!=xx[i-1])
xx[++M]=xx[i];
memcpy(x,xx,sizeof(xx));
memset(tree,0,sizeof(tree));
build(1,1,M);
last=0;
for(int i=1;i<cnt;i++)
{
int l=findp(1,M,lar[i].l);
int r=findp(1,M,lar[i].r)-1;
update(1,l,r,lar[i].f);
ans+=abs(last-tree[1].len);
last=tree[1].len;
}
ans+=last;
printf("%d\n",ans);
}
return 0;
}
扫描二维码关注公众号,回复:
5706519 查看本文章