2019杭电多校第⑨场B Rikka with Cake (主席树,离散化)

题意:

给定一块n*m的矩形区域,在区域内有若干点,每个顶点发出一条射线,有上下左右四个方向,问矩形被分成了几个区域?

思路:

稍加观察和枚举可以发现,区域数量=射线交点数+1(可以用欧拉定理验证,但是我不会),问题就转变为统计射线交点数量

可以将四个方向的射线分开,用左右的射线去查询与多少个上下的射线相交,先考虑向左的射线A与几条向上的射线相交,设A(x,y),即求(1,x)区间内\(\le y\)的向上的射线条数,显然可以利用主席树进行维护(也可以用树状数组并且更快,但是我不会)。其他情况同理,注意离散化时向上靠近还是向下靠近

由于y是在(1,1e9)范围内的,因此y需要离散化,因为主席树的性质,x必须排序离散化

//memory= 1.3e8 int
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e5+5;
const int N=1e5+2;
int xu[maxn],xd[maxn],yu[maxn],yd[maxn];
int x1[maxn],y1[maxn];
char c1[maxn];
const int Log=50;
int root[2][maxn],num[2][maxn*Log],lson[2][maxn*Log],rson[2][maxn*Log];
int tot[2];
double Sum,Num;
int build(int id,int l,int r){
    int root=++tot[id];
    num[id][root]=0;
    if(l<r){
        int mid=(l+r)>>1;
        lson[id][root]=build(id,l,mid);
        rson[id][root]=build(id,mid+1,r);
    }
    return root;
}
int update(int id,int pre,int l,int r,int x){
    int root=++tot[id];
    num[id][root]=num[id][pre]+1;
    lson[id][root]=lson[id][pre];
    rson[id][root]=rson[id][pre];
    if(l<r){
        int mid=(l+r)>>1;
        if(x<=mid) lson[id][root]=update(id,lson[id][pre],l,mid,x);
        else rson[id][root]=update(id,rson[id][pre],mid+1,r,x);
    }
    return root;
}
int query(int id,int Old,int New,int l,int r,int k){//Old和New对应旧版本的根和新版本的根
    if(id==0){//向上,查区间内<=k的数的个数
        if(r<=k){
            return num[id][New]-num[id][Old];
        }
        int mid=(l+r)>>1;
        int res=0;
        if(l<=k) res+=query(id,lson[id][Old],lson[id][New],l,mid,k);
        if(mid+1<=k) res+=query(id,rson[id][Old],rson[id][New],mid+1,r,k);
        return res;
    }
    else{//向下,查区间内>=k的数的个数
        if(l>=k){
            return num[id][New]-num[id][Old];
        }
        int mid=(l+r)>>1;
        int res=0;
        if(mid>=k) res+=query(id,lson[id][Old],lson[id][New],l,mid,k);
        if(r>=k) res+=query(id,rson[id][Old],rson[id][New],mid+1,r,k);
        return res;
    }
}
struct node{
    node(int a,int b):x(a),y(b){}
    int x,y;
};
bool cmp(node a,node b){
    return a.x<b.x;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        tot[0]=tot[1]=0;
        int n,m,k;
        vector<node> U,D,R,L;
        scanf("%d%d%d",&n,&m,&k);
        int x,y;
        char c;
        int sizexu=0,sizeyu=0,sizexd=0,sizeyd=0;
        for(int i=1;i<=k;i++){
            scanf("%d%d %c",&x,&y,&c);
            x1[i]=x;y1[i]=y;c1[i]=c;
            if(c=='U') xu[++sizexu]=x,yu[++sizeyu]=y;
            if(c=='D') xd[++sizexd]=x,yd[++sizeyd]=y;
        }
        sort(xu+1,xu+1+sizexu);sort(yu+1,yu+1+sizeyu);
        sort(xd+1,xd+1+sizexd);sort(yd+1,yd+1+sizeyd);
        sizexu=unique(xu+1,xu+1+sizexu)-(xu+1);
        sizexd=unique(xd+1,xd+1+sizexd)-(xd+1);
        sizeyu=unique(yu+1,yu+1+sizeyu)-(yu+1);
        sizeyd=unique(yd+1,yd+1+sizeyd)-(yd+1);
        for(int i=1;i<=k;i++){//U,D中存离散化的坐标值。L,R存原值,之后再在对应数组中二分找离散值
            if(c1[i]=='U'){
                x1[i]=lower_bound(xu+1,xu+1+sizexu,x1[i])-xu;
                y1[i]=lower_bound(yu+1,yu+1+sizeyu,y1[i])-yu;
                U.push_back(node(x1[i],y1[i]) );
            }
            if(c1[i]=='D'){
                x1[i]=lower_bound(xd+1,xd+1+sizexd,x1[i])-xd;
                y1[i]=lower_bound(yd+1,yd+1+sizeyd,y1[i])-yd;
                D.push_back(node(x1[i],y1[i]) );
            }
            if(c1[i]=='L') L.push_back(node(x1[i],y1[i]) );
            if(c1[i]=='R') R.push_back(node(x1[i],y1[i]) );
        }
        //建2棵主席树,分别存U,D,再用L和R取查找
        root[0][0]=build(0,1,N);
        root[1][0]=build(1,1,N);
        sort(U.begin(),U.end(),cmp);
        for(int i=0;i<U.size();i++)
            root[0][i+1]=update(0,root[0][i],1,N,U[i].y);
        sort(D.begin(),D.end(),cmp);
        for(int i=0;i<D.size();i++)
            root[1][i+1]=update(1,root[1][i],1,N,D[i].y);
        int ans=0;
        for(int i=0;i<L.size();i++){
            int r0=upper_bound(xu+1,xu+1+sizexu,L[i].x)-xu-1;//找<=,向左靠近
            int r1=upper_bound(xd+1,xd+1+sizexd,L[i].x)-xd-1;//找<=,向左靠近
            int h0=upper_bound(yu+1,yu+1+sizeyu,L[i].y)-yu-1;//找<=,向下靠近
            int h1=lower_bound(yd+1,yd+1+sizeyd,L[i].y)-yd;//找 >=,向上靠近
            ans+=query(0,root[0][0],root[0][r0],1,N,h0);
            ans+=query(1,root[1][0],root[1][r1],1,N,h1);
        }
        for(int i=0;i<R.size();i++){
            int l0=lower_bound(xu+1,xu+1+sizexu,R[i].x)-xu;//找>=,向右靠近
            int l1=lower_bound(xd+1,xd+1+sizexd,R[i].x)-xd;//找>=,向右靠近
            int h0=upper_bound(yu+1,yu+1+sizeyu,R[i].y)-yu-1;//找<=,向下靠近
            int h1=lower_bound(yd+1,yd+1+sizeyd,R[i].y)-yd;//找 >=,向上靠近
            ans+=query(0,root[0][l0-1],root[0][U.size()],1,N,h0);
            ans+=query(1,root[1][l1-1],root[1][D.size()],1,N,h1);
        }
        printf("%d\n",ans+1);
    }
}

原文地址:https://www.cnblogs.com/ucprer/p/11388097.html

时间: 2024-11-09 04:00:56

2019杭电多校第⑨场B Rikka with Cake (主席树,离散化)的相关文章

2019杭电多校第九场

2019杭电多校第九场 熟悉的后半场挂机节奏,又苟进首页了,很快乐 1001. Rikka with Quicksort upsolved 不是我做的,1e9调和级数分段打表 1002. Rikka with Cake solved at 01:11 有一个矩形,给你很多射线(射线只有横平竖直的四个方向),问把矩形切成了多少块 队友说答案是交点数加一,作为一个合格的工具人,当然是把队友的想法实现啦 二维坐标离散化枚举纵坐标维护横坐标,常规套路,树状数组也可以做(我是线段树写习惯了根本没想起来还有

2019 杭电多校 第二场

2019 Multi-University Training Contest 2 补题链接:2019 Multi-University Training Contest 2 1005 Everything Is Generated In Equal Probability (HDU-6595) 题意 给出一个整数 \(N\),在 \([1,N]\) 中随机生成一个 \(n\).然后生成长度为 \(n\) 的全排列 \([1, n]\). 对该排列运行一个程序,程序先求当前排列的逆序对对数,然后随

【2019.07.22】2019杭电多校第一场

补题地址:http://acm.hdu.edu.cn/listproblem.php?vol=56 题号:6578-6590 1001: 1002:线性基 https://blog.csdn.net/Cassie_zkq/article/details/96979461 1003: 1004: 1005:? 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 自闭场,但补题能学到好多算法和思维,继续加油鸭- 原文地址:https://www.cnblo

2019 杭电多校 第一场

2019 Multi-University Training Contest 1 补题链接:2019 Multi-University Training Contest 1 1002 Operation (HDU-6579) 题意 给定包含 \(n\) 个数的序列,\(m\) 个询问.询问有两种操作,操作 \(0\) 表示在数组最后添加一个新元素,操作 \(1\) 表示查询区间 [l,r] 的子集的异或最大值. 询问强制在线. 题解 线性基 贪心 1004 Vacation (HDU-6581)

2019 杭电多校 第九场

2019 Multi-University Training Contest 9 补题链接:2019 Multi-University Training Contest 9 1005 Rikka with Game (HDU 6684) 题意 Rikka 和 Yuta 玩游戏.给定一个字符串.两人轮流对字符串操作.可以选择结束游戏,也可以改变其中一个字符,改变规则是:\(a\rightarrow b,b\rightarrow c,-,y\rightarrow z,z\rightarrow a.\

2019 杭电多校第一场

D Vacation 题解:题目给出两个人要去旅行,在他们前面有n辆车,每辆车有长度以及车首部到stopline 的距离以及每辆车的最大速度,后面的车不能超过前面的车.问你他们两个的车首部到达stopline的最短时间. 思路:二分答案,求出最后一辆车停在的位置. 参考代码: #include<bits/stdc++.h> using namespace std; #define mkp make_pair<int,int> typedef long long ll; const

2019杭电多校第一场hdu6581 Vacation

Vacation 题目传送门 解题思路 一开始的时候所有车都是是按照自己原来的速度行驶,直到有一辆车x追上前面的那辆车y,此时的变化只有,1.x的速度变为y的速度2.x后面的车追上x的时间变短.所以我们只要利用优先队列,每一次都找到会在最短的时间内追上前面那辆车的x,不断更新这个过程就好了. 以及实现中的一些细节,1.对于已经追上和被追上的两辆车,我们可以看成一辆火车,每辆车就是一节车厢,其速度就是最前面那节车厢的速度,当一辆火车追上另一辆的时候,新的火车头就是前面被追上车厢的火车头,新的火车尾

[2019杭电多校第一场][hdu6582]Path

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6582 题意:删掉边使得1到n的最短路改变,删掉边的代价为该边的边权.求最小代价. 比赛时一片浆糊,赛后听到dinic瞬间思维通透XD 大致做法就是先跑一边最短路,然后再保留所有满足dis[i]+w==dis[j]的边,在这些边上跑最小割(dinic). 代码写的异常丑陋,见谅QAQ 1 #include<iostream> 2 #include<cstdio> 3 #include&l

2019杭电多校第一场

所有题解都在注释里面. 1004: 1 /* 2 出题人的说法:最终通过停车线的时候,一定是一个车后面堵着剩余所有车.那么影响 3 时间的就只有最前面这辆车,所以对于每一辆车,假设是它与0车堵在一起的最靠前的一辆车. 4 那么可以计算出一个值.所有的车的计算值的最大值就是答案. 5 这个就是出题人的说法 ,时间复杂度是o(n) 6 7 我个人的理解:我们做这道题得注意到一个条件,就是对当前的车,先不要考虑后面的车. 8 只考虑它与前面的那一辆车的互相作用.考虑他们什么时候撞倒. 9 首先假设第一