扫描线

扫描线

扫描线+离散化+线段树

(图片引用自@kk303的博客)

初始状态:

扫到最下面的线,点更新为红色部分:

扫到下一根线,求出绿色部分的面积,加入答案,更新计数器:

同上,求出黄色部分面积,加入答案:

同上,求出灰色部分面积,加入答案:

同上,求出紫色部分面积,加入答案:

同上,求出蓝色部分面积,加入答案:

扫描到最后一条边了,结束!

面积并

Luogu P5490 扫描线

就是扫描线,纯的

写法1(动态开点线段树):

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 200010
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int n,x1,x2,y1,y2,ans;
int cnt,X[maxn];
int t[2*maxn],tag[2*maxn],rt,ls[2*maxn],rs[2*maxn];
struct data{
	int l;
	int r;
	int h;
	int k;//权值1/-1 
}lin[maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;//********
	return x.h<y.h;
}

void pushup(int x,int l,int r){
	if(tag[x]) t[x]=X[r+1]-X[l];
	else t[x]=t[ls[x]]+t[rs[x]];
}

void update(int &x,int l,int r,int cl,int cr,int k){
	if(!x) x=++cnt;
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(ls[x],l,mid,cl,cr,k);
	if(cr>=mid+1) update(rs[x],mid+1,r,cl,cr,k);
	pushup(x,l,r);
}

signed main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x1),read(y1),read(x2),read(y2);
		X[i*2]=x1,X[i*2-1]=x2;
		lin[i*2]=(data){x1,x2,y1,1};
		lin[i*2-1]=(data){x1,x2,y2,-1};
	}
	sort(X+1,X+2*n+1);
	sort(lin+1,lin+2*n+1,cmp);
	int m=unique(X+1,X+2*n+1)-X-2;//去重 
	for(int i=1;i<=2*n;i++){
		int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
		int r=lower_bound(X+1,X+m+1,lin[i].r)-X-1;
		//--------------
//		cout<<l<<" "<<r<<endl;
//		cout<<rt<<endl;
//		cout<<t[rt]<<endl;
//		cout<<lin[i].h-lin[i-1].h<<endl;
//		cout<<"*****"<<endl;
		ans+=t[rt]*(lin[i].h-lin[i-1].h);
		update(rt,1,m,l,r,lin[i].k);
	} 
	cout<<ans<<endl;
	return 0;
}
/*
2
100 100 200 200
150 150 250 255
//
18000
*/

写法2(普通线段树):(下面都采用这种写法,注意空间要开大点)

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 800010//2*4=8倍空间? 
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int n,x1,x2,y1,y2,ans;
int X[maxn];
int t[2*maxn],tag[2*maxn];
struct data{
	int l;
	int r;
	int h;
	int k;//权值1/-1 
}lin[maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;
	return x.h<y.h;
}

void pushup(int x,int l,int r){
	if(tag[x]) t[x]=X[r+1]-X[l];
	else t[x]=t[x*2]+t[x*2+1];
}

void update(int x,int l,int r,int cl,int cr,int k){
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(x*2,l,mid,cl,cr,k);
	if(cr>=mid+1) update(x*2+1,mid+1,r,cl,cr,k);
	pushup(x,l,r);
}

signed main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x1),read(y1),read(x2),read(y2);
		X[i]=x1,X[i+n]=x2;
		lin[i]=(data){x1,x2,y1,1};
		lin[i+n]=(data){x1,x2,y2,-1};
	}
	sort(X+1,X+2*n+1);
	sort(lin+1,lin+2*n+1,cmp);
	int m=unique(X+1,X+2*n+1)-X-1;
	for(int i=1;i<=2*n-1;i++){
		int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
		int r=lower_bound(X+1,X+m+1,lin[i].r)-X;
		if(l<r) update(1,1,m,l,r-1,lin[i].k);
		ans+=t[1]*(lin[i+1].h-lin[i].h);
	}
	cout<<ans<<endl;
	return 0;
} 
/*
2
100 100 200 200
150 150 250 255
//
18000
*/

HDU 1542 Atlantis

也是板子,就不写了

面积交

HDU 1255

题目求的情况很好处理,覆盖至少两次的都可以,所以我们用 \(tag\) 来记录该节点表示的区间完全被覆盖。

\(tag \ge 2\) 表示该区间被完全覆盖 \(\ge 2\) 次,这正是我们需要的。 \(tag=1\) 表示被完全覆盖切覆盖长度就是区间长度。 \(tag=0\) 表示没被完全覆盖,但是可能覆盖一部分,所以要算出该区间中被覆盖的长度。

由此我们可以算出被覆盖一次和被覆盖两次的长度,我们用 \(one\)\(two\) 来存。

为什么这里存的只是长度呢?因为由扫描线的原理可得,我们是随着水平线的扫描来一块一块加入答案的,高度直接由相减可得,这里就只记录要加入的矩形的长度即可。

推广一下,若求覆盖3次,4次...也可以这样搞,当然次数太多就不行了。

  • 其实样例大概是有误差的,\(7.63->7.62\) 在这上面调一年

  • 对于题目中的 注意:本题的输入数据较多,推荐使用 \(scanf\) 读入数据. ,其实 \(cin\) 也是可过的(亲测

  • 空间要开够!别问我怎么知道的

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 4010//线段树4倍空间!!! 
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int t,n;
int tag[2*maxn];
double x1,x2,y1,y2,ans;
double X[maxn],one[2*maxn],two[2*maxn],ls[2*maxn],rs[2*maxn];
struct data{
	double l;
	double r;
	double h;
	int k;
}lin[2*maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;
	return x.h<y.h;
}

void pre(){
	ans=0;
	memset(tag,0,sizeof(tag));
	memset(one,0,sizeof(one));
	memset(two,0,sizeof(two));
}

void pushup(int x,int l,int r){
	if(tag[x]>=2) two[x]=one[x]=X[r+1]-X[l];
	if(tag[x]==1){
		one[x]=X[r+1]-X[l];
		if(l==r) two[x]=0;
		else two[x]=one[x*2]+one[x*2+1];
	}
	if(tag[x]==0){
		if(l==r) one[x]=two[x]=0;
		else{
			one[x]=one[x*2]+one[x*2+1];
			two[x]=two[x*2]+two[x*2+1];
		}
	}
}

void update(int x,int l,int r,int cl,int cr,int k){
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(x*2,l,mid,cl,cr,k);
	if(cr>=mid+1) update(x*2+1,mid+1,r,cl,cr,k);
	pushup(x,l,r);
}

signed main(){
	read(t);
	while(t--){
		read(n);
		for(int i=1;i<=n;i++){
			cin>>x1>>y1>>x2>>y2;
			X[i]=x1,X[i+n]=x2;
			lin[i]=(data){x1,x2,y1,1};
			lin[i+n]=(data){x1,x2,y2,-1};
		}
		sort(X+1,X+2*n+1);
		sort(lin+1,lin+2*n+1,cmp);
		pre();
		int m=unique(X+1,X+2*n+1)-X-1;
		for(int i=1;i<=2*n-1;i++){
			int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
			int r=lower_bound(X+1,X+m+1,lin[i].r)-X;
			//--------------
//			cout<<l<<" "<<r<<endl;
//			cout<<rt<<endl;
//			cout<<two[rt]<<endl;
//			cout<<lin[i].h-lin[i-1].h<<endl;
//			cout<<"*****"<<endl;
			if(l<r) update(1,1,m,l,r-1,lin[i].k);
			ans+=two[1]*(lin[i+1].h-lin[i].h);
		} 
		printf("%.2lf\n",ans);
	}
	return 0; 
}
/*
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
//
7.63
0.00
*/
/*
1
5
1 3 5 7
2 3 15 20
2.13 5.64 3 8
1 5 4 6
6 3 10 7
//
29.87
*/

周长并

HDU 1828 Picture

也可以去Luogu P1856提交

给个图感性理解一下(图片来自@TaoSama的博客)

所以x轴方向和y轴方向上各扫一遍然后加起来即可

  • 注意因为统计的是边,每一条边都要扫到,所以要扫到 \(2*n\) 而不是 \(2*n-1\)

  • 注意pushup里面的顺序

  • 因为边会重复覆盖,所以我们需要向答案中加入的是相邻两次修改的区间覆盖长度差

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 10010//线段树4倍空间!!! 
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int n;
int tag[2*maxn],t[2*maxn];
int x1,x2,y1,y2,m[2],ans;
int X[2][maxn];
struct data{
	int l;
	int r;
	int h;
	int k;
}lin[2][2*maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;
	return x.h<y.h;
}

void pre(){
	memset(tag,0,sizeof(tag));
	memset(t,0,sizeof(t));
}

void pushup(int id,int x,int l,int r){
//	if(l==r) t[x]=0;//注意!这样写是错的!想想为什么******** 
//	else{
//		if(tag[x]) t[x]=X[id][r+1]-X[id][l];
//		else t[x]=t[x*2]+t[x*2+1];
//	}
	if(tag[x]) t[x]=X[id][r+1]-X[id][l];
	else{
		if(l==r) t[x]=0;
		else t[x]=t[x*2]+t[x*2+1];
	}
}

void update(int id,int x,int l,int r,int cl,int cr,int k){
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(id,x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(id,x*2,l,mid,cl,cr,k);
	if(cr>=mid+1) update(id,x*2+1,mid+1,r,cl,cr,k);
	pushup(id,x,l,r);
}

signed main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x1),read(y1),read(x2),read(y2);
		X[0][i]=x1,X[0][i+n]=x2;
		X[1][i]=y1,X[1][i+n]=y2;
		lin[0][i]=(data){x1,x2,y1,1};
		lin[0][i+n]=(data){x1,x2,y2,-1};
		lin[1][i]=(data){y1,y2,x1,1};
		lin[1][i+n]=(data){y1,y2,x2,-1};
	}
	sort(X[0]+1,X[0]+2*n+1);
	sort(X[1]+1,X[1]+2*n+1);
	sort(lin[0]+1,lin[0]+2*n+1,cmp);
	sort(lin[1]+1,lin[1]+2*n+1,cmp);
	m[0]=unique(X[0]+1,X[0]+2*n+1)-X[0]-1;
	m[1]=unique(X[1]+1,X[1]+2*n+1)-X[1]-1;
	for(int i=0;i<=1;i++){
		int ans_tmp=0,lst=0;
		pre();
		for(int j=1;j<=2*n;j++){
			int l=lower_bound(X[i]+1,X[i]+m[i]+1,lin[i][j].l)-X[i];
			int r=lower_bound(X[i]+1,X[i]+m[i]+1,lin[i][j].r)-X[i];
			if(l<r) update(i,1,1,m[i],l,r-1,lin[i][j].k);
			ans_tmp+=abs(t[1]-lst);
			lst=t[1];
		}
		ans+=ans_tmp;
	}
	cout<<ans<<endl;
	return 0;
}
/*
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
//
228
*/

其他题目

  1. Get The Treasury
    [HDU 3642] Get The Treasury

  2. Stars in your window
    [POJ 2482] Stars in your window
    P1502 窗口的星星
    \(code\)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define N 100010
    #define int long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    int T,n,w,h;
    int X[N],t[N],tag[N],tot,ans,pre;
    struct node{
    	int x;
    	int y;
    	int l;
    }s[N];
    
    bool cmp(node a,node b){
    	if(a.x==b.x) return a.l>b.l;
    	return a.x<b.x;
    }
    
    void pushup(int x){
    	t[x]=max(t[x*2],t[x*2+1]);
    }
    
    void pushdown(int x){
    	if(tag[x]){
    		tag[x*2]+=tag[x];tag[x*2+1]+=tag[x];
    		t[x*2]+=tag[x];t[x*2+1]+=tag[x];
    		tag[x]=0;
    	}
    }
    
    void change(int x,int l,int r,int cl,int cr,int k){
    	if(cl<=l&&cr>=r){
    		tag[x]+=k;
    		t[x]+=k;
    		return ;
    	}
    	int mid=(l+r)/2;
    	pushdown(x);
    	if(cl<=mid) change(x*2,l,mid,cl,cr,k);
    	if(cr>=mid+1) change(x*2+1,mid+1,r,cl,cr,k);
    	pushup(x);
    }
    
    void build(int x,int l,int r){
    	t[x]=tag[x]=0;
    	if(l==r) return ;
    	int mid=(l+r)/2;
    	build(x*2,l,mid);
    	build(x*2+1,mid+1,r);
    }
    
    signed main(){
    	read(T);
    	while(T--){
    		tot=ans=0,pre=1;
    		read(n),read(w),read(h);
    		for(int i=1;i<=n;i++){
    			read(s[i].x),read(s[i].y),read(s[i].l);
    			X[++tot]=s[i].y;
    		}
    		sort(X+1,X+tot+1);
    		sort(s+1,s+n+1,cmp);
    		tot=unique(X+1,X+tot+1)-X-1;
    		build(1,1,tot);
    		for(int i=1;i<=n;i++){
    			while(s[i].x-s[pre].x>=w){
    			    int l=lower_bound(X+1,X+tot+1,s[pre].y)-X;
    	            int r=lower_bound(X+1,X+tot+1,s[pre].y+h)-X-1;
    	            change(1,1,tot,l,r,-s[pre].l);
    			    pre++;
    			} 
    			int l=lower_bound(X+1,X+tot+1,s[i].y)-X;
    	        int r=lower_bound(X+1,X+tot+1,s[i].y+h)-X-1;
    	        change(1,1,tot,l,r,s[i].l);
    			ans=max(ans,t[1]);
    		}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    /*
    2
    3 5 4
    1 2 3
    2 3 2
    6 3 1
    3 5 4
    1 2 3
    2 3 2
    5 3 1
    //
    5
    6
    */
    
posted @ 2021-07-31 10:48  DReamLion  阅读(344)  评论(1编辑  收藏  举报