2528:Mayor's posters:为什么要线段树?为什么要离散化?一文搞懂该题

题目大意

阿布扎比(Bytetown)的公民无法忍受,市长竞选活动的候选人一直在各地兴高采烈地张贴选举海报。 市议会最终决定建造一个选举墙来放置海报,并引入以下规则:

每个候选人都可以在墙上贴一张海报。

所有海报的高度等于墙的高度; 海报的宽度可以是任意整数个字节(byte是字节镇的长度单位)。

墙分为多个部分,每个部分的宽度为一个字节。

每个海报必须完全覆盖连续多个墙段。

他们建造了一堵10000000字节长的墙(以便有足够的空间容纳所有候选人)。 当竞选活动重新开始时,候选人将他们的海报放在墙上,并且海报的宽度差别很大。 此外,候选人开始将他们的海报放置在其他海报已经占据的墙段上。 Bytetown的每个人都很好奇,他们的海报在选举前的最后一天(全部或部分)可见。

您的任务是找到所有张贴者的可见张贴者的数目,并给出有关张贴者的尺寸,

思路分析

拿到题,看到这个区间覆盖,先是想到了线段树,有个基础的想法,对于每一个前来张贴的广告牌 l i r i l_i-r_i ,我们给区间中所有地方+1,最后的时候,对每一个广告牌区间查询 l i , r i l_i,r_i 的最小值,如果是1,就说明至少有一处没被覆盖。however,范围太太太太大了。叶子一千万的树,我连建的勇气都没有。。。怎么办呢,超大范围专用药:离散化。
在这里插入图片描述
这道问题难就难在离散化上,ԾㅂԾ,,我们简单的离散化是这样的:离散化的简介
如下面的例子(题目的样例),因为单位1是一个单位长度,将下面的

  1   2   3   4  6   7   8   10

     —  —  —  —  —  —  —  —

  1   2   3   4  5   6   7   8

离散化 X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10

于是将一个很大的区间映射到一个较小的区间之中了,然后再对每一张海报依次更新在宽度为1~8的墙上(用线段树),最后统计不同颜色的段数。

但是只是这样简单的离散化是错误的,

如三张海报为:1~10 1~4 6~10

离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

最终,1~2为2,3为1,4~5为3,于是输出正确结果3

好了知道怎么离散化,下来就是线段树的问题了,线段树教程+模板。这道题里我们显然不需要build操作,但是需要区间修改和区间查询。对数数组 s u m [ ] sum[] ,我们根据输入的顺序不断的操作输入区间 l i [ i ] , r i [ i ] li[i],ri[i] ,给这个区间附上标记 i i ,所有update函数变成了,

void pushdown(int rt)
{
    sum[rt<<1]=sum[rt];
    sum[rt<<1|1]=sum[rt];
    sum[rt]=-1;
}
void update(int L,int R,int C,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        sum[rt]=C;
        return ;
    }
    if(sum[rt]!=-1)
        pushdown(rt);
    int m=(l+r)>>1;
    if(m>=R) update(L,R,C,l,m,rt<<1);
    else if(L>m) update(L,R,C,m+1,r,rt<<1|1);
    else update(L,m,C,l,m,rt<<1),update(m+1,R,C,m+1,r,rt<<1|1);
}

如果包含了整个区间,就给这个区间覆盖此海报的标记(无论之前有没有),如果需要分区间,首先要把当前节点已有的标记pushdown,传给下层节点,然后继续在下层节点update。

同样的查询也要做相应的修改

void query(int l,int r,int rt)
{
        if(!vis[sum[rt]]&&sum[rt]!=-1)
        {
            ans++;
            vis[sum[rt]]=1;
        return ;
        }
    if(l==r)
    {
        return ;
    }
    if(sum[rt]!=-1)
        pushdown(rt);
    int m=(l+r)>>1;
    query(l,m,rt<<1);
    query(m+1,r,rt<<1|1);
}

如果一个节点被一个标记覆盖,说明这个标记是当前节点最后覆盖的海报,那么如果该海报还没有进行过统计,我们就ans++,否则二叉查询。

tips

  • 注意pushDown的时候,一定是被标记过的父节点才能pushdown,否则整棵树都会被push成0。
  • 注意区间长度,不重复元素个数,tree的查询/更新范围之间的关系。
  • 初始的root千万别是0
#include<math.h>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=10005;
int n;
int vis[maxn<<3],sum[maxn<<4];
int li[maxn*2],ri[maxn*2],lsh[maxn<<2];
void pushdown(int rt)
{
    sum[rt<<1]=sum[rt];
    sum[rt<<1|1]=sum[rt];
    sum[rt]=-1;
}
void update(int L,int R,int C,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        sum[rt]=C;
        return ;
    }
    if(sum[rt]!=-1)
        pushdown(rt);
    int m=(l+r)>>1;
    if(m>=R) update(L,R,C,l,m,rt<<1);
    else if(L>m) update(L,R,C,m+1,r,rt<<1|1);
    else update(L,m,C,l,m,rt<<1),update(m+1,R,C,m+1,r,rt<<1|1);
}
int ans;
void query(int l,int r,int rt)
{
        if(!vis[sum[rt]]&&sum[rt]!=-1)
        {
            ans++;
            vis[sum[rt]]=1;
        return ;
        }
    if(l==r)
    {
        return ;
    }
    if(sum[rt]!=-1)
        pushdown(rt);
    int m=(l+r)>>1;
    query(l,m,rt<<1);
    query(m+1,r,rt<<1|1);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(sum,-1,sizeof(sum));
        memset(vis,0,sizeof(vis));
        int tot=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&li[i],&ri[i]);
            lsh[tot++]=li[i];
            lsh[tot++]=ri[i];
        }
        sort(lsh,lsh+tot);
        int mm=unique(lsh,lsh+tot)-lsh;
        int tt=mm;
        for(int i=1;i<tt;i++)
        {
            if(lsh[i]-lsh[i-1]>1)
                lsh[mm++]=lsh[i-1]+1;
        }
        sort(lsh,lsh+mm);
        for(int i=0;i<n;i++)
        {
            int x=lower_bound(lsh,lsh+mm,li[i])-lsh;
            int y=lower_bound(lsh,lsh+mm,ri[i])-lsh;
            update(x,y,i,0,mm-1,1);
        }
        ans=0;
        query(0,mm-1,1);
        printf("%d\n",ans);
    }
}
发布了186 篇原创文章 · 获赞 13 · 访问量 9309

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105186519