【2019杭电多校第九场1002=HDU6681】Rikka with Cake(离散化+树状数组+思维)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6681

题目:


Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)

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≤109,1≤K≤105), 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≤xi<n,1≤yi<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<j≤K, 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 3while 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

解题思路:


重温此题。

题目限制:输入的这点的坐标x,y都互不相同,且不在边界上。

盲猜答案是交点个数+1(严谨证明有些复杂,此处略)。

 

思路也有些巧妙:

      

将左图转化为右图,当射线方向朝下时,分解成两个新的射线,如右图。

将这些射线按照起点的高度从低到高排列,高度相同时,竖直方向的射线排在前面。

依次处理拍完序后的射线,以右图为例:

当处理到蓝色虚射线时,射线B和射线A和这条蓝色虚射线都有交点,且交点的横坐标时左图C.x,标记C.x;

同理处理到射线D时,它和射线A、B也有交点,交点横坐标都是D.x,标记D.x;

接着处理射线A,射线A上可能对答案有贡献的点的横坐标的范围为[A.x,n-1],所以此时就需要求出[A.x,n-1]被标记的点的数目,添加到最终答案中;

再处理紫色虚射线,它和射线B有交点,交点横坐标为C.x,取消C.x的标记;

最后处理射线B, 射线B上可能对答案有贡献的点的横坐标的范围为[1, B.x],需要求出[1, B.x]内被标记的点数目,添加到答案中。

 因为我们在求交点个数的时候只考虑了横坐标,而横坐标的范围比较大,需要把需要用到的横坐标离散化(最多k+2个不同的横坐标)。标记的话可以直接标记1(单点更新),然后区间求和求出交点个数,所以可以用树状数组写,代码短好写。

总体思路:从下到上处理射线,转化后只存在朝上的射线。同高度时处理朝上的射线,再处理水平的射线。朝上的射线用来标记交点横坐标(树状数组单点更新),水平的射线用来统计答案(树状数组区间求和)。

注意:

(1)方向朝下的射线需要特殊处理,转成两条朝上的射线。可能有大量的朝下的射线,所以存射线的数组需开到2*maxn,否则会T(?)。

(2)当射线朝左时,1,x 都是需要考虑的横坐标; 当射线朝右时,x,n-1也都是需要考虑的横坐标;离散化的时候去重就可以。可能有大量的水平方向的射线,所以存横坐标的数组应该开到2*maxn

 

ac代码:


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) ((x)&(-x))
const int maxn = 2e5+10;
struct node{
    int hor, l, r, height, val;//hor为1表示水平
    friend bool operator < (node a, node b)
    {
        return  a.height == b.height ? a.hor < b.hor : a.height < b.height;//从低到高开始处理,同高度先处理竖直方向的射线
    }
}Line[maxn];
int n, m, k, numl, numx;
int xx[maxn], c[maxn]={0};

void update(int x, int v)
{
    for(int i = x; i <= numx; i += lowbit(i))
        c[i] += v;
}
ll getsum(int x)
{
    ll ans = 0;
    for(int i = x; i > 0; i -= lowbit(i))
        ans += c[i];
    return ans;
}
int get_rank(int x)
{
    return lower_bound(xx+1, xx+numx+1, x) - xx;
}
ll solve()
{
    ll ans = 0;
    sort(Line, Line+numl);
    sort(xx+1, xx+numx);
    numx = unique(xx+1, xx+numx) - xx;//去重后的元素个数
    //cout << "numx: " << numx << endl;
    numx--;//不同的元素个数
    for(int i =1; i <= numx; i++) c[i] = 0;
    for(int i = 0; i < numl; i++)
    {
        // printf("%d %d %d\n",Line[i].l, Line[i].r, Line[i].height);
        if(Line[i].hor == 0)//竖直方向,的R无意义
            update(get_rank(Line[i].l), Line[i].val);
        else
            ans += getsum(get_rank(Line[i].r)) - getsum(get_rank(Line[i].l)-1);
    }
    return ans + 1;
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int x, y;
        char ch;
        numl = 0, numx = 1;
        scanf("%d %d %d", &n, &m, &k);
        for(int i = 1; i <= k; i++)
        {
            scanf("%d %d %c", &x, &y, &ch);
            if(ch == 'U') Line[numl++] = (node){0, x, -1, y, 1};
            if(ch == 'D')
            {
                Line[numl++] = (node){0, x, -1, 0, 1};
                Line[numl++] = (node){0, x, -1, y+1, -1};
            }
            if(ch == 'L')
            {
                xx[numx++] = 1;
                Line[numl++] = (node){1, 1, x, y, 0};
            }
            if(ch == 'R')
            {
                xx[numx++] = n-1;
                Line[numl++] = (node){1, x, n-1, y, 0};
            }
            xx[numx++] = x;
        }
        printf("%lld\n", solve());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值