POJ - 2528 线段树+离散化

其实很早就在白书上的常用技巧 看到离散化的操作,但是之前一直没遇到过需要离散化的题目(应该是我太菜的缘故),所以一直也没怎么重视,下面说说这道题目的考点,也就是离散化

什么是离散化呢?请先自行百度理解了,一定先了解后再往下看。

 

那么该如何进行操作呢?

 

举个例子

假如 我们有5个数

 36   63431  986357  850159901    2147483640 

很明显  它们的大小关系为   36 < 63431 < 986357  < 850159901 < 2147483640

 

但是,我们并不需要知道 36   63431  986357  850159901    2147483640 它们实际的大小,只需要知道它们的相对大小就可以了

 

所以我们可以把  36转化为 1  63431转化为2  986357转化为3  850159901转化为4  2147483640转化为5

 

这样我们得到了新的5个数 也就是 1 2 3 4 5

同时,它们的相对大小还是一样的  1<2<3<4<5 

 

再用题目中的数据来举个例子

1
5
1 4
2 6
8 10
3 4
7 10

 

我们得到了5个范围,也就是1~4, 2~6, 8~10, 3~4, 7~10.

同样的,我们并不需要知道它们的实际范围,只需要知道它们的相对范围就可以了

我们把它们每个点都整合起来  所以就是   1 2 3 4 4 6 7 8 10 10

然后去掉重复的点 剩下 1 2 3 4 6 7 8 10

离散化得到新的范围 1~4,  2~5, 7~8, 3~4, 6~8

画个图会发现,新的范围 和 旧的范围  的  相对范围,其实是一样的.

 

下面是这道题的思路:

  线段树方面是常规操作,关键是这个离散化就有点特殊了

  首先我们测试下这个数据

  1
  3
  1 10
  1 4
  6 10

  会发现常规的离散化会覆盖点5这个点,而得到答案2,但是应该是3才对

  这时候我们需要把每个点的右边的数加入  离散化的操作中来保证 连续性

  (但是这个题目的数据太弱,导致错误的离散化也可以AC)

#include <algorithm>
#include <iostream>
#include<sstream>
#include<iterator>
#include<cstring>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<deque>
#include<map>
#include<set>

#define M 100005
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;

int T, n;
int x, y, z;
set<int>ans;
vector<int>num;

struct Data {
    int l, r, val, lazy;
}tree[8*M];

struct Data_x {
    int u, v;
}loc_[M], loc[M];//loc_c是最初的数据,loc是离散后的数据

void built(int l, int r, int k) {
    tree[k].l = l, tree[k].r = r;
    tree[k].val = tree[k].lazy = 0;
    if (l == r)return;
    int mid = (l + r) / 2;
    built(l, mid, k << 1);
    built(mid + 1, r, (k << 1) | 1);
}

void down(int k) {
    tree[k << 1].val = tree[k << 1].lazy = tree[k].lazy;
    tree[(k << 1) | 1].val = tree[(k << 1) | 1].lazy = tree[k].lazy;
    tree[k].lazy = 0;
}

void change(int k) {
    if (tree[k].l >= x && tree[k].r <= y) {
        tree[k].val = z;
        tree[k].lazy = z;
        return;
    }
    if (tree[k].lazy)down(k);
    int mid = (tree[k].l + tree[k].r) / 2;
    if (x <= mid)change(k << 1);
    if (y > mid)change((k << 1) | 1);
    if (tree[k << 1].val == tree[(k << 1) | 1].val) tree[k].val = tree[k << 1].val;
    else tree[k].val = -1;
}

void cal(int k) {
    if (tree[k].val>0) {
        ans.insert(tree[k].val);
        return;
    }
    if (!tree[k].val) return;
    cal(k << 1);
    cal((k << 1) | 1);
}

int main()
{
    cin >> T;
    while (T--) {
        cin >> n;
        ans.clear();
        num.clear();
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", &loc_[i].u, &loc_[i].v);//读入最初的数据、
            //把所有的点整合在一起
            num.push_back(loc_[i].u);
            num.push_back(loc_[i].v);
            //保证连续性
            num.push_back(loc_[i].u + 1);
            num.push_back(loc_[i].v + 1);
        }

        sort(num.begin(), num.end());
        vector<int>::iterator iter = unique(num.begin(), num.end());//去重

        //得到新的范围
        for (int i = 1; i <= n; i++) {
            loc[i].u = lower_bound(num.begin(), iter,loc_[i].u) - num.begin();
            loc[i].v = lower_bound(num.begin(), iter, loc_[i].v) - num.begin();
            loc[i].u++;
            loc[i].v++;
        }
        
        built(1, M, 1);
        for (int i = 1; i <= n; i++) {
            x = loc[i].u, y = loc[i].v, z = i;
            change(1);
        }
        cal(1);
        cout << ans.size() << endl;
    }
    return 0;
}

 

全部评论

相关推荐

11-01 08:48
门头沟学院 C++
伤心的候选人在吵架:佬你不要的,能不能拿户口本证明过户给我。。球球了
点赞 评论 收藏
分享
11-05 18:26
门头沟学院 Java
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务