洛谷题单指南-前缀和差分与离散化-P1496 火烧赤壁

原题链接:https://www.luogu.com.cn/problem/P1496

题意解读:给定n个区间[a,b),计算所有区间覆盖的总长度。

解题思路:

方法1、离散化

先思考一种比较直观的思路:

既然要计算多个区间覆盖的总长度,可以枚举每一个区间[a,b),通过一个桶数组来标记区间中所有的点f[x] = 1,最终统计所有为1的点数量,即可得到总长度。

但是,要注意此题区间[a,b)中a,b的取值范围−2^31≤𝑎<𝑏<2^31,无法通过桶数组来标记,会导致内存超限。

怎么解决呢?这里就要借助离散化的方法!

所谓离散化,就是用数字的相对值替代他们的绝对值,能够将分布广而稀疏的数字转为连续的、密集的分布,从而起到节省空间的效果。

对于本问题,尽管a,b取值范围较大,但是区间不超过20000个,也就是a,b的值最多40000个,我们可以建立一个1~40000的数值与真实a、b之间的映射关系,如样例:建立一个c[]数组,编号与a,b数值对应关系如下

c[] 1 2 3 4 5 6
a,b数值 -1 1 2 5 9 11

我们通过桶数组标记所有[-1,1),[5,11),[2,9)的值对应的编号为1

[-1,1):对应编号[1,2),f[1] = 1

[5,11):对应编号[4,6),f[4] = f[5] = 1

[2,9):对应编号[3,5),f[3] = f[4] = 1

综上:

f[] 1 2 3 4 5 6
1 0 1 1 1 0

再通过枚举f[]数组计算覆盖的总长度,对于每一个f[i] = 1,其所表示在真实区间上的数值覆盖长度是开始c[i+1]-c[i]

那么,如何计算c[]数组,也就是如何建立离散化之后的有序数字与原始a,b数值之间的映射关系?

可以如下处理:

第一步、将a[]、b[]所有的数字保存到新的数组c[]

第二步、对c[]进行排序、去重

第三步、对a[]、b[]中每一个数字,通过二分在c[]中查找第一出现的下标,并用下标更新a[],b[]中原来的数字

即对a[],b[]中每一个数字完成了离散化处理。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 20005;

int n;
int a[N], b[N], c[2 * N], cnt;
int f[2 * N];
long long ans;

int bs(int x)
{
    int l = 1, r = cnt, res = -1;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(c[mid] >= x) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    return res;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        c[++cnt] = a[i];
        c[++cnt] = b[i];
    }

    //排序
    sort(c + 1, c + cnt + 1);
    //去重
    int j = 0;
    for(int i = 1; i <= cnt; i++)
        if(i == 1 || c[i] != c[i - 1]) 
            c[++j] = c[i];
    cnt = j;
    //将a,b离散化
    for(int i = 1; i <= n; i++)
    {
        int x = bs(a[i]);
        int y = bs(b[i]);
        //标记着火的点
        for(int j = x; j < y; j++)
            f[j] = 1;
    }
    //枚举着火点,计算对应真实位置的长度
    for(int i = 1; i <= cnt; i++)
    {
        if(f[i])
        {
            ans += c[i + 1] - c[i];
        }
    }
    cout << ans;

    return 0;
}

方法2、区间合并

参考:https://www.cnblogs.com/jcwy/p/18208437 里介绍的区间合并方法。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 20005;

struct node
{
    int a, b;
    bool operator < (const node &x) const
    {
        return a < x.a;
    }
};
node range[N];
vector<node> newrange;
int n, ans;

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> range[i].a >> range[i].b;
    }
    //将区间按左端点从小到大排序
    sort(range + 1, range + n + 1);
    
    //以下进行区间合并
    node cur = range[1]; //初始区间为第一个区间
    for(int i = 2; i <= n; i++)
    {
        if(range[i].a > cur.b)
        {
            newrange.push_back(cur);
            cur = range[i];
        }
        else
        {
            cur.b = max(cur.b, range[i].b);
        }
    }
    newrange.push_back(cur);

    //计算区间覆盖长度
    for(auto i : newrange)
    {
        ans += i.b - i.a;
    }
    cout << ans;

    return 0;
}

 

posted @ 2024-07-28 14:42  五月江城  阅读(21)  评论(0编辑  收藏  举报