hdu6681 Rikka with Cake(扫描线,线段树)

题意:

在n*m的坐标系平面上有k条射线,射线有上下左右四种不同的方向,
问这些射线把这个平面切成多少块?
坐标系的左下角为(0,0),右上角为(n,m).

数据范围:n,m<=1e9,k<=1e5,保证不存在一条射线直接将矩形切成两块的情况hdu6681 Rikka with Cake(扫描线,线段树)_第1张图片

解法:

答案就是交点个数+1
那么对于每条横线,计算出有多少条竖线穿过就行
用线段树扫描线的思想,将射线拆分为修改和询问.
用竖着的扫描线从x=0向右扫:
1.遇到横线的左端点就加入线段树,
2.遇到横线的右端点就消除,
3.遇到竖线就统计线段树中有多少个点和这个竖线相交

线段树大小和纵坐标范围有关,因此对纵坐标离散化.

code:

#include
using namespace std;
const int maxm=1e5+5;
int a[maxm<<2];
int b[maxm];
int n,m,k;
//
struct Node{
    int x,y1,y2,op;
    bool operator<(const Node& a)const{
        if(x!=a.x)return x<a.x;
        return op<a.op;
    }
}e[maxm<<2];
//
void init(int l,int r,int node){//清空树
    a[node]=0;
    if(l==r)return ;
    int mid=(l+r)/2;
    init(l,mid,node*2);
    init(mid+1,r,node*2+1);
}
void update(int x,int val,int l,int r,int node){
    if(l==r){
        a[node]+=val;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(x,val,l,mid,node*2);
    else update(x,val,mid+1,r,node*2+1);
    a[node]=a[node*2]+a[node*2+1];
}
int ask(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r)return a[node];
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans+=ask(st,ed,l,mid,node*2);
    if(ed>mid)ans+=ask(st,ed,mid+1,r,node*2+1);
    return ans;
}
signed main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        int tot=0;
        int num=0;
        for(int i=1;i<=k;i++){
            int x,y;char s[2];
            scanf("%d%d%s",&x,&y,s);
            b[++num]=y;
            if(s[0]=='L'){
                e[++tot]={0,y,0,1};
                e[++tot]={x+1,y,0,2};
            }else if(s[0]=='R'){
                e[++tot]={x,y,0,1};
                e[++tot]={n+1,y,0,2};
            }else if(s[0]=='U'){
                e[++tot]={x,y,m,3};
            }else if(s[0]=='D'){
                e[++tot]={x,0,y,3};
            }
        }
        b[++num]=0,b[++num]=m;
        sort(e+1,e+1+tot);
        sort(b+1,b+1+num);
        num=unique(b+1,b+1+k+2)-b-1;
        init(1,num,1);
        int ans=1;//记得+1
        for(int i=1;i<=tot;i++){
            if(e[i].op==1){//左边,加入
                int x=lower_bound(b+1,b+1+num,e[i].y1)-b;
                update(x,1,1,num,1);
            }else if(e[i].op==2){//右边,删除
                int x=lower_bound(b+1,b+1+num,e[i].y1)-b;
                update(x,-1,1,num,1);
            }else if(e[i].op==3){//竖线,查询
                int l=lower_bound(b+1,b+1+num,e[i].y1)-b;
                int r=lower_bound(b+1,b+1+num,e[i].y2)-b;
                ans+=ask(l,r,1,num,1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(hdu6681 Rikka with Cake(扫描线,线段树))