2019暑假杭电多校赛第九场B.Rikka with Cake(线段树)

更多多校信息←请点击这里

Problem Description

传送门
Rikka’s birthday is on June 12th. The story of this problem happens on that day.
Today is Rikka’s birthday. Yuta prepares a big cake for her: the shape of this cake is a rectangular of n centimeters times m centimeters. With the guidance of a grimoire, Rikka is going to cut the cake.
For simplicity, Rikka firstly builds a Cartesian coordinate system on the cake: the coordinate of the left bottom corner is (0,0) while that of the right top corner is (n,m). There are K instructions on the grimoire: The ith cut is a ray starting from (xi,yi) while the direction is Di. There are four possible directions: L, passes (xi−1,yi); R, passes (xi+1,yi); U, passes (xi,yi+1); D, passes (xi,yi−1).
Take advantage of the infinite power of Tyrant’s Eye, Rikka finishes all the instructions quickly. Now she wants to count the number of pieces of the cake. However, since a huge number of cuts have been done, the number of pieces can be very large. Therefore, Rikka wants you to finish this task.
Input
The first line of the input contains a single integer T(1≤T≤100), the number of the test cases.
For each test case, the first line contains three positive integers n,m,K ( 1 ≤ n , m ≤ 1 0 9 , 1 ≤ K ≤ 1 0 5 ) (1≤n,m≤10^9,1≤K≤10^5) (1n,m109,1K105), which represents the shape of the cake and the number of instructions on the grimoire.
Then K lines follow, the ith line contains two integers xi,yi ( 1 ≤ x i &lt; n , 1 ≤ y i &lt; m ) (1≤xi&lt;n,1≤yi&lt;m) (1xi<n,1yi<m)and a char Di∈{‘L’,‘R’,‘U’,‘D’}, which describes the ith cut.
The input guarantees that there are no more than 5 test cases with K>1000, and no two cuts share the same x coordinate or y coordinate, i.e., ∀ 1 ≤ i &lt; j ≤ K ∀1≤i&lt;j≤K 1i<jK, xi≠xj and yi≠yj.
Output
For each test case, output a single line with a single integer, the number of pieces of the cake.
Hint
The left image and the right image show the results of the first and the second test case in the sample input respectively. Clearly, the answer to the first test case is 3 while the second one is 5.
在这里插入图片描述
Sample Input
2
4 4 3
1 1 U
2 2 L
3 3 L
5 5 4
1 2 R
3 1 U
4 3 L
2 4 D
Sample Output
3
5


题目大意:
在一个n*m的矩形内,有k个点,每个点都有一个方向(U,R,D,L)代表从这个点开始根据这个方向切割这个矩形。问最后这个矩形被切割成多少部分?


题目思路:
听说权值线段树和树状数组也可以ac。
这里是最简单的区间加法的普通线段树。
通过观察题目很容易发现就是求射线交点的个数
由于x,y很大,但是n不大,所以一定有离散化操作。
这里我们离散化y轴。
我们先求L和U,D的交点
然后按x的坐标从小到大,依次将U和D的点加入线段树。每加入一个点x,1-x的区间+1.当遇到L,查询L点离散化后的y坐标(单点查询)即可。
也就是:
假设
tree 0 0 0 0 0 0
(D)x=2时,有y=3
tree 1 1 1 0 0 0
(U)x=3时,有y=3
tree 1 1 2 1 1 1
(L)x=4时,有y=3
ans+=2;
(L)x=5时,有y=4
ans+=1;
因为xi≠xj and yi≠yj.所以直接按x排序插入线段树即可。晚插入的L的x一定长于先插入的。
同理可得L和U,D的交点。(按x从大到小)


AC_code:

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+1000;
ll tree[N<<2],lzadd[N<<2];
void init(){
    memset(tree,0,sizeof(tree));
    memset(lzadd,0,sizeof(lzadd));
}
void PushDown(int root,int ln,int rn){
    if(lzadd[root]){
        lzadd[root<<1]+=lzadd[root];
        lzadd[root<<1|1]+=lzadd[root];
        tree[root<<1]+=lzadd[root]*ln;
        tree[root<<1|1]+=lzadd[root]*rn;
        lzadd[root]=0;
    }
}
void Update(int l,int r,int L,int R,int root) { //D:Update(1,id,1,k,1); (1~id)+1  
    int m=(L+R)>>1; 							//U:Update(id,k,1,k,1); (id~k)+1
    PushDown(root,m-L+1,R-m);
    if(l<=L&&R<=r) {                            
        tree[root]+=(R-L+1);
        lzadd[root]+=1;
        return;
    }
    if(l<=m)
        Update(l,r,L,m,root<<1);
    if(r>m)
        Update(l,r,m+1,R,root<<1|1);
    tree[root]=tree[root<<1]+tree[root<<1|1];
}

ll query(int x,int l,int r,int root){ //query(id,1,k,1); 单点查询
    int m=(r+l)>>1;
    PushDown(root,m-l+1,r-m);
    if(l==r){
        return tree[root];
    }
    if(x<=m)
        return query(x,l,m,root<<1);
    else
        return query(x,m+1,r,root<<1|1);
}

int t,n,m,k;
struct node { //点的信息
    int x,y;
    char c;
    bool operator<(const node &a)const {
        return x<a.x;
    }
}no[N];
vector<int>v;
int getid(int x){
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d",&n,&m,&k);
        init(),v.clear();
        for(int i=1;i<=k;i++) {
            scanf("%d%d %c",&no[i].x,&no[i].y,&no[i].c);
            v.push_back(no[i].y);  //存y点的坐标
        }
        sort(no+1,no+k+1); //总坐标先对x排序,
        sort(v.begin(), v.end()); //对y坐标排序(离散化操作)
        ll ans=1;
        //算向右的与上下的交点
        //因为xi≠xj and yi≠yj,所以不会有点在同一直线上。否则排序需要先U和D在L和R
        for(int i=1;i<=k;i++) { //按x从小到大
            int id=getid(no[i].y); //离散化
            if(no[i].c=='U') 
                Update(id,k,1,k,1); //(id~k)+1
            else if(no[i].c=='D')
                Update(1,id,1,k,1); //(1~id)+1
            else if(no[i].c=='L')
                ans+=query(id,1,k,1);  //查询id点的值
        }
        init(); //初始化
        //算向左的与上下的交点
        for(int i=k;i>0;i--){  //按x从大到小
            int id=getid(no[i].y);
            if(no[i].c=='U')
                Update(id,k,1,k,1);
            else if(no[i].c=='D')
                Update(1,id,1,k,1);
            else if(no[i].c=='R')
                ans+=query(id,1,k,1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值