POJ2528 Mayor's posters(区间更新、暴力统计叶子结点、离散化)

题目链接

http://poj.org/problem?id=2528

题目

n(n<=10000) 个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000) 。求出最后还能看见多少张海报。
Input
第一行: 样例个数T
第二行: 贴海报的人n
第三行: 每个人贴海报的范围
接下来n行: 每个人贴海报的范围
Output
对于每一个输入,输出最后可以看到的海报张数。

Sample Input
1
5
1 4
2 6
8 10
3 4
7 10
Sample Output
4

分析

数据范围有1e7,需要对数据进行离散化。
离散过程:
排序、相邻数相差大于1在其中插入一个值、用下标代替值。比一般的离散化多了一个插入值的步骤,这是因为如果相邻数之差大于1,表示其中有空位,离散化后变成连续的数,表示没有空位,含义上有所改变。故要插入一个中间值,使含义不变。
一开始用map去实现离散化,超时了,很迷。参考网上博客用二分去写,79msA了,orz。
此题的线段树不用维护结点信息,因为延迟标记就代表了结点信息。故不需要up操作去回溯更新父结点。统计结果时暴力统计叶子结点的颜色情况即可,将延迟标记传递到每个叶子结点,保证叶子结点的正确性。

AC代码

//79ms 2.7MB
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <map>
#define lson p<<1
#define rson p<<1|1
using namespace std;

const int maxn=2e5+100;
struct node
{
    int l,r;//结点所维护的区间
    int mark;//延迟标记
}T[maxn<<2];//线段树要开四倍空间
int x[maxn],y[maxn],a[maxn];
int vis[maxn];//叶子染色的颜色
//map<int,int> mp;//map超时?

void down(int p)
{
    if(!T[p].mark) return ;
    T[lson].mark=T[rson].mark=T[p].mark;
    T[p].mark=0;
}
void build(int p,int l,int r)//p表示结点编号,l、r表示结点p所表示的区间
{
    T[p].mark=0;
    T[p].l=l,T[p].r=r;//确定结点p所表示的区间[l,r]
    if(l==r) //确定叶子结点所表示的信息
    {
        return ;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid); //递归创建左子树
    build(rson,mid+1,r); //递归创建右子树
}
void update(int p,int x,int y,int v)
{
    if(T[p].l==x && T[p].r==y) //区间恰好重合
    {
        T[p].mark=v; //标记信息修改到此结点打止
        return ;
    }
    //区间在p的子树结点中,要调用子树结点信息,必须先下传延迟标记
    down(p); //下传标记将p的左右子结点修改,只修改了这两个结点,延迟标记放在了这两个结点上
    int mid=(T[p].l+T[p].r)>>1;
    if(y<=mid) //区间[x,y]一定在左子树上
        update(lson,x,y,v);
    else if(x>mid) //区间[x,y]一定在右子树上
        update(rson,x,y,v);
    else
    {
        //必须将区间分成两份小区间[x,mid]和[mid+1,y]
        update(lson,x,mid,v);
        update(rson,mid+1,y,v);
    }
}
void query(int p)
{
    if(T[p].l==T[p].r)
    {
        vis[T[p].mark]=1;
        return ;
    }
    down(p);
    query(lson);
    query(rson);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        //mp.clear();
        int m,tot=0,n=0;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            a[tot++]=x[i];
            a[tot++]=y[i];
        }
        sort(a,a+tot);
        tot=unique(a,a+tot)-a;
        int t=tot;
        for(int i=1;i<t;i++)
            if(a[i]-a[i-1]>1)
                a[tot++]=a[i-1]+1;
        sort(a,a+tot);
        build(1,1,tot);
        for(int i=1;i<=m;i++)
        {
            int l=lower_bound(a,a+tot,x[i])-a+1;
            int r=lower_bound(a,a+tot,y[i])-a+1;
            //cout<<l<<" "<<r<<endl;
            update(1,l,r,i);
        }
        query(1);//将延迟标记传递到每个叶子结点
        int ans=0;
        for(int i=1;i<=m;i++)
            if(vis[i]) ans++;
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值