【2019 HDU多校集训第九场】 Rikka with Cake 线段树

题意:
给出一个左下角为 ( 0 , 0 ) (0,0) (0,0),右上角为 ( n , m ) (n,m) (n,m)的矩形,然后给你 k k k条射线,每条射线的起点及方向(上下左右)都已知,射线的起点都位于矩形内,不包括矩形边界,且每条射线间不会有重叠,求矩形被分割成多少块。
思路:
用线段树解,题中说是射线,所以我们可以对y轴离散化,对x轴排序后用线段树维护y轴坐标,遇到向上或向下的射线,则在线段树区间上加一,当遇见向左的射线时,对y轴进行单点查询然后ans加上这个结果。然后从右到左再处理一遍,与上面相同。

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 2e5 + 1000;
struct node {
    ll sum[N << 2], add[N << 2];
    void init() {
        memset(sum, 0, sizeof sum);
        memset(add, 0, sizeof add);
    }
    void pushup(int rt) {
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
    void PushDown(int rt, int ln, int rn) {
        if(add[rt]) {
            add[rt << 1] += add[rt];
            add[rt << 1 | 1] += add[rt];
            sum[rt << 1] += add[rt] * ln;
            sum[rt << 1 | 1] += add[rt] * rn;
            add[rt] = 0;
        }
    }
    void Update(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) {
            sum[rt] += (r - l + 1);
            add[rt] += 1;
            return;
        }
        int m = (l + r) >> 1;
        PushDown(rt, m - l + 1, r - m);
        if(L <= m)
            Update(L, R, l, m, rt << 1);
        if(R >  m)
            Update(L, R, m + 1, r, rt << 1 | 1);
        pushup(rt);
    }
    ll query(int x, int l, int r, int rt) {
        if(l == r) {
            return sum[rt];
        }
        int m = (r + l) >> 1;
        PushDown(rt, m - l + 1, r - m);
        if(x <= m)
            return query(x, l, m, rt << 1);
        else
            return query(x, m + 1, r, rt << 1 | 1);
    }
} tree;
int t, n, m, k;
struct nod {
    int x, y;
    char c;
    bool operator <(const nod &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);
        tree.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);
        }
        sort(no + 1, no + k + 1);
        sort(v.begin(), v.end());
        ll ans = 1;
        for(int i = 1; i <= k; i++) {
            int id = getid(no[i].y);
            if(no[i].c == 'U')
                tree.Update(id, k, 1, k, 1);
            else if(no[i].c == 'D')
                tree.Update(1, id, 1, k, 1);
            else if(no[i].c == 'L')
                ans += tree.query(id, 1, k, 1);
        }
        tree.init();
        for(int i = k; i > 0; i--) {
            int id = getid(no[i].y);
            if(no[i].c == 'U')
                tree.Update(id, k, 1, k, 1);
            else if(no[i].c == 'D')
                tree.Update(1, id, 1, k, 1);
            else if(no[i].c == 'R')
                ans += tree.query(id, 1, k, 1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值