离散化—区间覆盖——线段树实践POJ2528



离散化——目的是压缩区间范围——优化线段树

区间覆盖问题——传统的cover数组再加上——这个题目的特殊性——计算的是最终能看到海报的数量——所以节点的值在这里我的含义是第几张海报覆盖了这个节点
好,看一下准备工作

#include 
#include 
#include 
using namespace std;
#define lson i << 1,left,mid
#define rson i << 1 | 1,mid + 1,right
const int Max = 20010;
int ans;
int vis[Max];
struct Line{
    int point;
    int num;
}line[Max];
bool cmp(Line a,Line b)
{
    return a.point < b.point;
}
int poster[Max / 2][2];
int t[Max << 2];

根据题目开20000+

vis数组,line结构体,cmp函数,poster都是为了离散化处理并存储海报x轴的左右端点

t树组就是树了

 for(int i = 1;i <= n;i++)
            {
                scanf("%d%d",&poster[i][0],&poster[i][1]);
                line[i * 2 - 1].point = poster[i][0];
                line[i * 2 - 1].num = -i;//左
                line[i * 2].point = poster[i][1];
                line[i * 2].num = i;//右
            }
            sort(line + 1,line + 2 * n + 1,cmp);
            int flag = -0x3f3f;
            int N = 0;
            for(int i = 1;i <= 2 * n;i++)
            {
                if(line[i].point != flag)
                {
                    N++;
                    flag = line[i].point;

                }
                if(line[i].num < 0)poster[-line[i].num][0] = N;
                else poster[line[i].num][1] = N;
            }

离散化要为每一个节点都赋值,所以要2 * n

结构体的point的存储的是原来的x坐标,num存储的是一种映射| i |代表第几张海报,如果point为左端点,那么做个标记-i右端点为+i;

最后自定义排好序后,定义一个映射——N
对于每一个原来的端点都能映射回去,对于每一个不重复的端点N++

最后得到的N就是线段树需要的维护的宽度

接着常规的建树

void build(int i,int left,int right)
{
    t[i] = 0;
    if(left == right)return;
    int mid = (left + right) >> 1;
    build(lson);
    build(rson);
}

然后开始从头到尾贴海报

for(int i = 1;i <= n;i++)
            {
                //cout<

update要更新节点的值,和lazy标记的思想一样,我们不会把海报覆盖的长度一直维护到叶子节点,一旦找到最小包容区间就会停止,在更新这张海报的过程中,会顺便传递前面海报的lazy标记

void pd(int i)
{
    if(t[i])
    {
        t[i << 1] = t[i];
        t[i << 1 | 1] = t[i];
        t[i] = 0;
    }
}
void update(int i,int left,int right,int le,int re,int v)
{
    if(le <= left && right <= re )
    {
        t[i] = v;
        return;
    }
    pd(i);
    int mid = (left + right) >> 1;
    if(le <= mid) update(lson,le,re,v);
    if(re > mid)update(rson,le,re,v);
}

定义了vis数组开始查询能看到几张,就是代表能找到几个值不同的节点

void query(int i,int left,int right)
{

    if(t[i] != 0)
    {

        if(!vis[t[i]])
        {

            vis[t[i]] = 1;
            ans++;
        }
        return;
    }
    if(left == right)return;
    int mid = (left + right) >> 1;
    query(lson);
    query(rson);
}

注意这里从上往下找,一旦找到了不为零的节点的值,那么就可以return了,因为他的子节点没有必要再去往下找了,那些都是已经被覆盖的地方了




你可能感兴趣的:(算法入门)