zoukankan      html  css  js  c++  java
  • 扫描线

    扫描线

    扫描线+离散化+线段树

    (图片引用自@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
    ",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
      ",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
      */
      
  • 相关阅读:
    汇编/操作系统 索引帖
    极地网络
    河中跳房子游戏
    搬家大吉QAQQAQ
    【USACO3.1.1】Agri-Net最短网络
    浅谈二叉树
    Android面试经验汇总(二)
    Android面试经验汇总(一)
    Android 聊天室(二)
    Android 聊天室(一)
  • 原文地址:https://www.cnblogs.com/DReamLion/p/14924694.html
Copyright © 2011-2022 走看看