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

    ioi1998 Picture
    在一个平面上放置一些矩形,所有的边都为垂直或水平。每个矩形可以被其它矩形部分或完全遮盖,所有矩形合并成区域的边界周长称为轮廓周长。
    要求:计算轮廓周长。
    数据规模:
    0≤矩形数目<5000;
    坐标数值为整数,范围是[-10000,10000]。
    Input
    第一橫列是牆上所貼的長方形圖片總數。之後每一橫列是一個長方形的左下角與右上角的整數座標。各座標的x值在前,y值在後。
    Output
    應有一非負整數,即為長方形圖片聯集體的總周長
    Sample Input
    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
    Sample Output
    228

    扫描线算法:
    将线段放到线段树上,每个矩形两条竖线,两条横边,情况是一样的。
    对于两条竖边,左边的为入边,右边的为出边。
    遇到左边的边,染色次数tim为1.投到线段线上。
    线段树上相应的区间当染色次数tim为0变1,或者1变成0时,才统计结果。
    对于下图
    先将1,2这两条统计进来
    再加入蓝色这个长条矩形时
    对于3条边,直接加上,因为染色次数tim是从0变成1
    对于4条边,不能加入,因为染色次数tim是从1变成2
    对于5条边,直接加上,因为染色次数tim是从0变成1
    对于6条边,不能加入,因为染色次数tim是从1变成2
    对于7条边,直接加上,因为染色次数tim是从0变成1
    下面讲下染色次数tim的变化
    当对某条线段进行染色次数tim变化时,要检查下其是否是一条“统一”的线段
    即这条线段的子线段被染色的次数是一样的。例如
    加入两个小矩形后,[a,b]是不连续的

    当长竖矩形加入后
    对于整条线段[a',b],它开始是不能处理的,因为对应的线段状态不统一
    于是递归下去
    到了[a',c']这一段是能处理的,tim从0变1
    到了[c',d']这一段是能处理的,tim从1变2
    于是对于[a',d']这一段的状态也是不统一的
    当处理线段[a'',d'']时也是要分开进行处理的
    即分别处理[a'',c'']让线段的染色次数tim从1变成0,进行累加
    再处理[c'',d'']让线段的染色次数tim从2变1,不进行累加
    一句话概括就是:
    将一条线段分成若干段,每段的tim代表被染色的次数。只有次数一样时这一段的状态就是统一的(即不为-1).否则各有各的染色次数。只有当染色次数从0变1,或者1变0时才计数

    //先将每条边抽离出来,例如对于(a,b),(c,d)两个点组成的矩形,其中(a,b)是左下角,(c,d)是右上角
    //则有一条竖着的进入边覆盖区间(b,d),它是在x轴上a点时进入的
    //其对应的出边也是覆盖(b,d),它是在x轴上c点时离开的
    //将所有竖边按x轴上的顺序升序排好,如果值一样,则进边在前,出边在后
    //然后不断加边进去,将对应的区间从0变成1时,或1变成0时统计长度,其它值的变化时并不统计长度
     
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=1e4;
    int tim[10*N],lazy[10*N],ans;
    struct momo
    {
        int begin,end,mark,place;
    };
    bool cmp(momo x,momo y)
    {
        if(x.place!=y.place)
    	    return x.place<y.place;
        return x.mark>y.mark;
    //按坐标轴从小到大,如果坐标轴一样则进入边在前,出去的边在后面 
    }
    void pushdown(int num)
    {
        if(lazy[num])
        {
            if(tim[num]!=-1)//如果整个线段状态是统一的,即要么全覆盖,要么全没有覆盖 
    		    tim[num]+=lazy[num];
            lazy[num*2]+=lazy[num];
            lazy[num*2+1]+=lazy[num];
            lazy[num]=0;
        }
    }
    void change(int num,int l,int r,int x,int y,int v)
    {
        if(l>=x&&r<=y&&tim[num]!=-1)
        {
            if((tim[num]==1&&v==-1)||(tim[num]==0&&v==1))
            //边长只有在从0变1时,以及 1变0时才进行统计 
    		    ans=ans+r-l;
            lazy[num]+=v;
    		pushdown(num);
            return;
        }
        int mid=(l+r)/2;
        pushdown(num*2);
    	pushdown(num*2+1);
        if(x<mid)
    	    change(num*2,l,mid,x,y,v);
        if(y>mid)
    	    change(num*2+1,mid,r,x,y,v);
        if(tim[num*2]==tim[num*2+1]) //统一一下标计 
    	    tim[num]=tim[num*2];
        else
            tim[num]=-1;
    }
    momo high[N+100],wide[N+100];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            //对于所有竖边,从左向向看,左边的边是进入边,右边的边是出边 
            high[i].mark=1;   //进入的边 
    	    high[i].place=a;  //统计竖边时,对应x轴 
    		high[i].begin=b;  //开始位置 
    		high[i].end=d;    //结束位置 
    		high[i+n].mark=-1;  //出去的边 
            high[n+i].place=c;  //统计横边时,对应y轴 
    		high[n+i].begin=b;
    		high[n+i].end=d;
    		//对于所有横边,从下向上看,下面的边是进入边,上面的出边 
            wide[i].mark=-1;
            wide[i].place=d;
    		wide[i].begin=a;
    		wide[i].end=c;
    		
    		wide[n+i].mark=1;
            wide[n+i].place=b;
    		wide[n+i].begin=a;   
    		wide[n+i].end=c;
        }
        sort(high+1,high+2*n+1,cmp);
        sort(wide+1,wide+2*n+1,cmp);
        ans=0;
        for(int i=1;i<=2*n;i++)
               change(1,-N,N,high[i].begin,high[i].end,high[i].mark);
        for(int i=1;i<=2*n;i++)
               change(1,-N,N,wide[i].begin,wide[i].end,wide[i].mark);
        printf("%d
    ",ans);
        return 0;
    }
    

      

    #include<bits/stdc++.h>
    #define int long long
    #define ls (p<<1)
    #define rs (ls|1)
    #define mid ((l+r)>>1)
    #define N 10001
    using namespace std;
    int tree[16*N],tag[16*N],ans;
    struct AC{int val,l,r,id;}h[N],s[N];
    bool cmp(AC a,AC b){return a.val==b.val?a.id>b.id:a.val<b.val;}
    void pushdown(int p)
    {
        if(tag[p])//如果有lazy标记 
    	{
            if(tree[p]!=-1)//如果这一段的状态是统一的 
    		    tree[p]+=tag[p];
            tag[ls]+=tag[p];//左右两个子树都加上  
            tag[rs]+=tag[p];
            tag[p]=0;  
        }
    }
    void update(int p)
    {
        if(tree[ls]==tree[rs])tree[p]=tree[ls];
        else tree[p]=-1;
    }
    void change(int p,int l,int r,int L,int R,int k){
        if(L<=l&&r<=R&&tree[p]!=-1)
    	{
            if((tree[p]==1&&k==-1)||(tree[p]==0&&k==1))ans+=r-l;
            tree[p]+=k,tag[ls]+=k,tag[rs]+=k;
            //整统出现次数加上K,同时左右子树标记也加上K 
            return;
        }
        pushdown(ls),pushdown(rs);//这一句还要理解下 
        if(L<mid)change(ls,l,mid,L,R,k);
        if(R>mid)change(rs,mid,r,L,R,k);
        update(p);
    }
    signed main(){
        int n;
        scanf("%lld",&n);
        for(int i=1,a,b,c,d;i<=n;i++){
            scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
            h[i].val=a,h[i].l=b,h[i].r=d,h[i].id=1;
            h[i+n].val=c,h[i+n].l=b,h[i+n].r=d,h[i+n].id=-1;
            s[i].val=d,s[i].l=a,s[i].r=c,s[i].id=-1;
            s[i+n].val=b,s[i+n].l=a,s[i+n].r=c,s[i+n].id=1;
        }
        sort(h+1,h+1+2*n,cmp);
        sort(s+1,s+1+2*n,cmp);
        ans=0;
        for(int i=1;i<=2*n;i++)change(1,-N,N,s[i].l,s[i].r,s[i].id);
        for(int i=1;i<=2*n;i++)change(1,-N,N,h[i].l,h[i].r,h[i].id);
        printf("%lld",ans);
        return 0;
    }
    

      

  • 相关阅读:
    接口问题
    鉴权 授权 验签
    adb常用命令
    cookie session
    常见http返回状态码
    Linux下mysql数据库的命令
    Linux课堂笔记--第九天
    Linux课堂随笔 -第八天
    Linux课堂笔记-第七天
    Linux课堂随笔-第六天
  • 原文地址:https://www.cnblogs.com/cutemush/p/13339216.html
Copyright © 2011-2022 走看看