POJ 1201 Intervals 线段树 / 平方分割

一、题目大意

给我们一些闭区间[ai , bi],其中 1 <= ai <= bi <= 50000,让我们求出一个集合,使得这个集合与 区间 [ai , bi]有 ci个共同元素,对于所有的 1<=i <=n个区间而言。

二、解题思路

1、线段树

根据题目范围,我们可以对 1到50000的数字进行循环,然后每次循环中用线段树进行操作,O(n*logn)的复杂性时间上可行。

1、我们可以把所有的区间按照区间的起点进行排序,然后计算出每个区间的 bi - ci + 1,用线段树维护各个区间的 bi - ci + 1的最小值。

2、针对每次循环的数字 num,只需要用二分确定 ai 小于等于它的范围 R,然后从线段树中查询出其中最小的 bi - ci + 1,如果 bi - ci + 1 == num,代表这个数字需要被选择,答案加一,同时需要更新线段树上的[0 , R) 内所有的 bi - ci + 1 值为原来的值 + 1

3、每次循环 num后,判断 num 是否等于某个区间的 bi,如果等于,则将对应的 bi 在线段树上的节点的 bi - ci + 1 设置为50007(50000以上则为废弃节点,因为大于所有的num),然后循环向上更新父节点即可。

本题目的线段树需要实现以下两个功能。

1、查询 [0 , R)的 bi - ci + 1的最小值

2、更新[0, R)范围内所有的 bi - ci + 1

那么我们需要让线段树上保存两个值

1、[0, R)区间内集体加上的值(dat)

2、[0 , R)内去掉第1点剩余部分的最小值(rmq)。

这样对于查询操作时,只需要加上所有父节点及自己的 dat,和自身的rmq即可。

对于更新操作时,只需要更新自己的dat,

并递归所有父节点的 rmq[parent]=min(rmq[lch]+dat[lch],rmq[rch]+dat[rch])即可。

总结:每次利用RMQ求出 a1 <=  num范围内的 bi - ci + 1的最小值,与num进行比较,即可知道这个数字是需要选择,接下来在树上废弃掉 bi <= num的节点即可不会被已经经过的区间影响,同时如果这个数字被选择,更新 a1 <= num的全部节点 为原来的值+1,这样代表这些区间内都加入了一个有效元素,以为下次循环做铺垫。

2、平方分割

平方分割的思路和线段树相同,把50000个区间按照 ai 排序,每225个分成一个桶,维护每个桶内 bi - ci + 1 的最小值,和这个桶同时加上的元素个数。

设i 从 [0 , max(bi)] 循环,每次找出小于等于 i 的元素区间 [0 , R),利用桶计算[0 , R)中 bi - ci + 1的最小值,如果这个最小值等于 i,则代表 i 位置需要放一个元素,答案加一,然后对 [0 , R)区间内集体 + 1,如果对于在桶内的元素,只需要更新这个桶的维护的值即可,对于不完全包含在桶内的,需要对这个元素单独处理。

更新了一个桶的部分(非全部)元素时,需要重新计算这个桶的最小值。

然后每次循环的末尾,通过二分判断 i 是否等于某个 bi,如果等于,需要把这个元素(或几个)置为失效,然后更新对应的桶即可,平方分割实现上的难度比线段树简单一点,但是也要慢一点。

三、代码

1、线段树

#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
P sortByA[50007], sortByB[50007];
int rmq[131072], dat[131072], a[50009], b[50009], c[50009], n, _current, maxt, ans;
void input()
{
    scanf("%d", &n);
    maxt = 0, ans = 0;
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d%d", &a[i], &b[i], &c[i]);
        maxt = max(maxt, b[i]);
        sortByA[i].first = a[i];
        sortByA[i].second = i;
    }
    sort(sortByA, sortByA + n);
    for (int i = 0; i < n; i++)
    {
        sortByB[i].first = b[sortByA[i].second];
        sortByB[i].second = i;
    }
    sort(sortByB, sortByB + n);
}
void build(int i, int l, int r)
{
    if (r - l == 1)
    {
        rmq[i] = b[sortByA[l].second] - c[sortByA[l].second] + 1;
    }
    else
    {
        int lch = i * 2 + 1;
        int rch = i * 2 + 2;
        build(lch, l, (l + r) / 2);
        build(rch, (l + r) / 2, r);
        rmq[i] = min(rmq[lch], rmq[rch]);
    }
}
int query(int _l, int _r, int i, int l, int r)
{
    if (_l >= r || _r <= l)
    {
        return 50007;
    }
    else if (l >= _l && r <= _r)
    {
        return rmq[i] + dat[i];
    }
    else
    {
        if (dat[i] > 50000)
        {
            return dat[i];
        }
        int lch = query(_l, _r, i * 2 + 1, l, (l + r) / 2);
        int rch = query(_l, _r, i * 2 + 2, (l + r) / 2, r);
        return min(lch, rch) + dat[i];
    }
}
void update(int _l, int _r, int i, int l, int r, int v)
{
    if (_l >= r || _r <= l)
    {
    }
    else if (l >= _l && r <= _r)
    {
        dat[i] += v;
        int j = i;
        while (j > 0)
        {
            j = (j - 1) / 2;
            rmq[j] = min(rmq[j * 2 + 1] + dat[j * 2 + 1], rmq[j * 2 + 2] + dat[j * 2 + 2]);
        }
    }
    else
    {
        update(_l, _r, i * 2 + 1, l, (l + r) / 2, v);
        update(_l, _r, i * 2 + 2, (l + r) / 2, r, v);
    }
}
void solveItem()
{
    int idx = upper_bound(sortByA, sortByA + n, P(_current, 0x3f3f3f3f)) - sortByA;
    if (idx <= 0)
    {
        return;
    }
    int mint = query(0, idx, 0, 0, n);
    if (mint == _current)
    {
        update(0, idx, 0, 0, n, 1);
        ans++;
    }
    int past = lower_bound(sortByB, sortByB + n, P(_current, -0x3f3f3f3f)) - sortByB;
    for (int j = past; j < n; j++)
    {
        if (sortByB[j].first != _current)
        {
            break;
        }
        update(sortByB[j].second, sortByB[j].second + 1, 0, 0, n, 50007);
    }
}
void solve()
{
    for (int i = 0; i <= maxt; i++)
    {
        _current = i;
        solveItem();
    }
}
int main()
{
    input();
    build(0, 0, n);
    solve();
    printf("%d\n", ans);
    return 0;
}

2、平方分割

#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
const int B = 225;
int bkt[230], addBkt[230], add[50007], a[50007], b[50007], c[50007], n, _currentMin, maxt, idx2SortA[50007];
P sortByA[50007], sortByB[50007];
bool removed[50007];
void input()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d%d", &a[i], &b[i], &c[i]);
        sortByA[i].first = a[i];
        sortByA[i].second = i;
        sortByB[i].first = b[i];
        sortByB[i].second = i;
    }
    sort(sortByA, sortByA + n);
    sort(sortByB, sortByB + n);
    maxt = sortByB[n - 1].first;
    for (int i = 0; i < n; i++)
    {
        idx2SortA[sortByA[i].second] = i;
    }
}
void flushBkt(int idx)
{
    int L = (idx / B) * B;
    int R = L + B;
    R = (R > n ? n : R);
    bkt[idx / B] = 50007;
    for (int i = L; i < R; i++)
    {
        if (!removed[sortByA[i].second])
        {
            bkt[idx / B] = min(bkt[idx / B], add[i] + b[sortByA[i].second] - c[sortByA[i].second] + 1);
        }
    }
}
void queryBkt(int _R, char opType)
{
    int bktR = _R - (_R % B);
    bktR = (_R == n ? n : bktR);
    for (int i = bktR; i < _R; i++)
    {
        if (opType == 'q' && !removed[sortByA[i].second])
        {
            _currentMin = min(_currentMin, addBkt[i / B] + add[i] + b[sortByA[i].second] - c[sortByA[i].second] + 1);
        }
        if (opType == 'u')
        {
            add[i] = add[i] + 1;
        }
    }
    if (bktR != _R && opType == 'u')
    {
        flushBkt(bktR);
    }
    for (int i = 0; i < bktR; i = i + B)
    {
        if (opType == 'q')
        {
            _currentMin = min(_currentMin, addBkt[i / B] + bkt[i / B]);
        }
        if (opType == 'u')
        {
            addBkt[i / B] = addBkt[i / B] + 1;
        }
    }
}
void solve()
{
    for (int i = 0; i < n; i = i + B)
    {
        flushBkt(i);
    }
    int idx = 0, ans = 0;
    for (int i = 0; i <= maxt; i++)
    {
        _currentMin = 50007;
        idx = upper_bound(sortByA, sortByA + n, P(i, 0x3f3f3f3f)) - sortByA;
        queryBkt(idx, 'q');
        if (_currentMin == i)
        {
            queryBkt(idx, 'u');
            ans++;
        }
        idx = lower_bound(sortByB, sortByB + n, P(i, -0x3f3f3f3f)) - sortByB;
        for (int j = idx; j < n; j++)
        {
            if (sortByB[j].first != i)
            {
                break;
            }
            removed[sortByB[j].second] = true;
            flushBkt(idx2SortA[sortByB[j].second]);
        }
    }
    printf("%d\n", ans);
}
int main()
{
    input();
    solve();
    return 0;
}

四、运行情况

1、线段树

2、平方分割

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值