FZU 2163 多米诺骨牌(线段树)

题目链接:
FZU 2163 多米诺骨牌
题意:
一条水平线上竖直放置 n 个多米诺骨牌,给出每个骨牌的坐标x[i]和高度 y[i] 。每个骨牌向右倒会砸到某些多米诺骨牌,如果多米诺骨牌(初始坐标 x[i] 和高度 y[i] )倒下,会导致所有在 [x[i]+1x[i]+y[i]1] 范围内的多米诺骨牌倒下。求每个骨牌最多可以砸倒多少骨牌?
数据范围: n105,x[i][108,108],y[i][2,108]
分析:
ans[i] 表示第 i 个多米诺骨牌可以砸倒的最多多米诺骨牌数量。那么ans[i]是坐标 x[j][x[i],x[i]+y[i]1] max(ans[j]+ji) 。我们可以先按照横坐标排序,从后往前求解,用线段树更新结点 ans[j]+j ,求区间最大值。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <climits>
#define lson(x) (x << 1)
#define rson(x) ((x << 1) | 1)
using namespace std;
typedef long long ll;
const int MAX_N = 100010;

int n, top;
int ans[MAX_N], sta[MAX_N], extra[MAX_N];

struct Domino {
    int x, y, id;

    bool operator < (const Domino& rhs) const {
        return x < rhs.x;
    }
}domino[MAX_N];

struct SegTree {
    int left, right, value;
}segtree[MAX_N << 2];

void build(int left, int right, int cur)
{
    segtree[cur].left = left, segtree[cur].right = right;
    if (left == right) {
        segtree[cur].value = left + 1;
        return;
    }
    int mid = (left + right) >> 1;
    build(left, mid, lson(cur));
    build(mid + 1, right, rson(cur));
    segtree[cur].value = max(segtree[lson(cur)].value, segtree[rson(cur)].value);
}

int query(int a, int b, int cur)
{
    int left = segtree[cur].left, right = segtree[cur].right;
    if (left == a && right == b) {
        return segtree[cur].value;
    }
    int mid = (left + right) >> 1;
    if (b <= mid) return query(a, b, lson(cur));
    else if (a > mid) return query(a, b, rson(cur));
    else {
        return max(query(a, mid, lson(cur)), query(mid + 1, b, rson(cur)));
    }
}

void update(int goal, int cur, int value)
{
    int left = segtree[cur].left, right = segtree[cur].right;
    if (left == right) {
        segtree[cur].value = value;
        return;
    }
    int mid = (left + right) >> 1;
    if (goal <= mid) update(goal, lson(cur), value);
    else update(goal, rson(cur), value);
    segtree[cur].value = max(segtree[lson(cur)].value, segtree[rson(cur)].value);
}

int main()
{
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; ++i) {
            scanf("%d%d", &domino[i].x, &domino[i].y);
            domino[i].id = i;
        }
        sort(domino, domino + n);
        build(0, n - 1, 1);
        for (int i = 0; i < n; ++i) {
            extra[i] = domino[i].x;
        }
        top = 0;
        ans[domino[n - 1].id] = 1;
        int x, y, pos, last, cur, value;
        for (int i = n - 2; i >= 0; --i) {
            x = domino[i].x, y = domino[i].y, cur = domino[i].id;
            if (x + y > extra[n - 1]) pos = n - 1;
            else {
                pos = lower_bound(extra, extra + n, x + y) - extra - 1;
            }
            ans[cur] = query(i, pos, 1) - i;
            update(i, 1, ans[cur] + i);
            //printf("i = %d pos = %d cur = %d ans[cur] = %d\n", i, pos, cur, ans[cur]);
        }
        for (int i = 0; i < n; ++i) {
            if (i) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值