D - Mayor's posters POJ - 2528 线段树 + 离散化(写一篇博客纪念一下)

https://cn.vjudge.net/problem/POJ-2528

一个十分经典的题,想用自己的法子离散化,硬是wa了40多发还没能过,整个人都要自闭了,贼难受。最后无奈用了别人的方法,好不爽。。。

我在此主要讲一下离散化的方法(当然是别人的方法啦),此题完整的代码放在最后!!!!

1.先把每条线段的单独存下来,同时把每个端点的值存在另一个数组里,在此把图放上

        for(int i = 1; i <= n; ++ i)
        {
            cin >> li[i] >> ri[i];
            x[cnt++] = li[i];
            x[cnt++] = ri[i];
        }

2.来一发排序,sort即可,然后处理一下相邻数据的差超过1的情况。先解释一下为什么,线段树是以点的形式存储了线段,所以难免会有局限,比如区间[1, 4]和[5, 6], 在线段数里面其实是相连接的,也就是说中间没有空隙(注意!!!),按照普通的离散化,两个不连续的数离散化之后会是连续的数,比如[1, 4], [2, 8], [7, 10]这三条线段,离散后:[1, 3], [2, 5], [4, 6], 然后算算离散前后的结果,一个是3, 一个是2,所以说就要把相差超过1的数据的特点体现出来,使得其之间有空隙。方法就是在这两个数中间再加一个数。就是x[i - 1]++这个操作了,最后再来发sort就ok了

        sort(x + 1, x + cnt);
        int m = cnt;
        for(int i = 2; i < cnt; ++ i)
        {
            if(x[i] - x[i - 1] > 1)
            {
                x[m++] = x[i - 1]++;
            }
        }
        sort(x + 1, x + m);

3.然后把数组中重复的元素去掉,因为接下来我们要用二分查找来确定每条线段的标号,去重才能保证标号唯一。

        int top = 2;
        y[1] = x[1];
        for(int i = 2; i < m; ++ i)
        {
            if(x[i] != x[i - 1])
            {
                y[top++] = x[i];
            }
        }

4.下来就是通过二分查找把每条线段离散后的范围和标号对应起来,进行区间更新。

 for(int i = 1; i <= n; ++ i)
 {
     int L = Bin(1, top - 1, li[i]);
     int R = Bin(1, top - 1, ri[i]);
     Update(L, R, i, 1, top - 1, 1);
 }


另外附上二分查找的函数

 inline int Bin(int l, int r, int x)
{
    int m;
    while(l < r)
    {
        m = (l + r) / 2;
        if(y[m] == x)
            return m;
        else if(y[m] > x)
            r = m - 1;
        else
            l = m + 1;
    }
    return l;
}

最后再查询一波就好了

下面上完整代码:

再解释一下我的线段树,我用-1代表此区间没有颜色或者不是纯色的情况(反正这两种情况都差不多,不能直接统计)

尽我努力,希望能对你有帮助。。发现有错误也欢迎积极评论^_^

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10003;
int Tree[8 * N], li[4 * N], ri[4 * N], x[4 * N], y[4 * N], sum;
bool vis[4 * N];

inline int Bin(int l, int r, int x)
{
    int m;
    while(l < r)
    {
        m = (l + r) / 2;
        if(y[m] == x)
            return m;
        else if(y[m] > x)
            r = m - 1;
        else
            l = m + 1;
    }
    return l;
}

void Update(int L, int R, int op, int l, int r, int rt)
{
    if(l >= L && r <= R)
    {
        Tree[rt] = op;
        return ;
    }
    if(Tree[rt] != -1)
    {
        int ans = Tree[rt];
        Tree[rt*2] = ans;
        Tree[rt*2+1] = ans;
        Tree[rt] = -1;
    }
    int m = (l + r) >> 1;
    if(m >= L)
        Update(L, R, op, l, m, rt << 1);
    if(m < R)
        Update(L, R, op, m + 1, r, rt << 1 | 1);
}


void Query(int l, int r, int rt)
{
    if(Tree[rt] != -1)
    {
        if(!vis[Tree[rt]])
        {
            vis[Tree[rt]] = true;
            sum++;
        }
        return ;
    }
    if(l == r)
        return ;
    int m = (l + r) / 2;
    Query(l, m, rt << 1);
    Query(m + 1, r, rt << 1 | 1);
}


int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        cin >> n;
        memset(vis, false, sizeof(vis));
        memset(Tree, -1, sizeof(Tree));
        int cnt = 1;
        for(int i = 1; i <= n; ++ i)
        {
            cin >> li[i] >> ri[i];
            x[cnt++] = li[i];
            x[cnt++] = ri[i];
        }

        sort(x + 1, x + cnt);
        int m = cnt;
        for(int i = 2; i < cnt; ++ i)
        {
            if(x[i] - x[i - 1] > 1)
            {
                x[m++] = x[i - 1]++;
            }
        }
        sort(x + 1, x + m);
        int top = 2;
        y[1] = x[1];
        for(int i = 2; i < m; ++ i)
        {
            if(x[i] != x[i - 1])
            {
                y[top++] = x[i];
            }
        }
        for(int i = 1; i <= n; ++ i)
        {
            int L = Bin(1, top - 1, li[i]);
            int R = Bin(1, top - 1, ri[i]);
            Update(L, R, i, 1, top - 1, 1);
        }
        sum = 0;
        Query(1, top - 1, 1);
        cout << sum << endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值