poj 2528 Mayor's posters 线段树+离散化 区间更新

线段树第三天,结果就这么个题重写了两遍才过

首先,题目中说的墙的长度是10000000,如果用这个长度建线段树,一定是超内存的,所以需要离散化,具体的就是对输入的点的坐标进行排序,然后算出他们分别是第几小,用这个第几大去替换原来的值就好,如 1,5,8 可以替换成1,2,3因为线段(1,5)和(1,8)的关系与(1,2)(1,3)其实是一样的

离散化之后,那么线段树的长度最多就是2*n 了

此题中,线段树节点记录当前区间能看到的海报值,如果当前区间能看到多张海报或者看不到,那么就取0,否则就是海报值

在update线段树的时候,如果遇到(L<=l&&R>=r) 则直接将该区间的sum值更新,否则,先push_down一下,然后再更新左右子树,这里的push_down就是把父节点的值赋给子节点,如果父节点为0则不操作,这样做的原因是:每次我们更新只是更新到区间而不是叶子,所以当我们要对一个已经更新过的区间的子区间进行更新时,必须把这个区间的信息往下传递,否则,他的子区间就算被更新了,他的值没变,依然代表整个区间内还是这个海报

query的时候,还得注意一个问题,就是一张海报可能在多个区间段内,因此得有个标记表示该海报应被算过了,f数组就是这个功能

找到sum值大于0的时候,如果该海报没被访问,就返回1,否则0,否则,如果找到叶子了,就直接返回,如果不是这些情况,就继续对左右子树进行query

以下是代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 20010;
int sum[MAX<<2];
bool f[MAX];
void push_down(int rt)
{
    if(sum[rt])
    {
        sum[rt<<1] = sum[rt];
        sum[rt<<1|1] = sum[rt];
        sum[rt] = 0;
    }
}
void build(int l,int r,int rt)
{
    if(l==r) return;
    int mid = l+r>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
void update(int p,int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        sum[rt] = p;
        return;
    }
    int mid = r+l>>1;
    push_down(rt);
    if(L<=mid) update(p,L,R,l,mid,rt<<1);
    if(R>mid) update(p,L,R,mid+1,r,rt<<1|1);
}
int query(int L,int R,int l,int r,int rt)
{
    if(sum[rt])
    {
        if(!f[sum[rt]])
        {
            f[sum[rt]] = 1;
            return 1;
        }
        return 0;
    }
    if(l==r) return 0;
    int mid = r+l>>1;
    int ans = 0;
    if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
    if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
    return ans;
}
struct point //这是为了离散化用的
{
    int x,y;
}p[MAX];
int cmp(const point &a,const point &b)
{
    if(a.x==b.x)
    return a.y<b.y;
    return a.x<b.x;
}
int x[MAX]; //离散化的结果,即p[i].x是第几小
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0; i<n*2; i++)
        {
            scanf("%d",&p[i].x);
            p[i].y = i;
        }
        sort(p,p+n*2,cmp);
        int cnt = 1;
        int now = p[0].x;
        x[p[0].y] = 1;
        for(int i=1; i<n*2; i++)
        {
            if(p[i].x==now) x[p[i].y] = cnt;
            else
            {
                x[p[i].y] = ++cnt;
                now = p[i].x;
            }
        }
        //上面是离散化
        build(1,cnt,1);
        memset(sum,0,sizeof(sum));
        memset(f,0,sizeof(f));
        for(int i=0; i<n; i++)
        update(i+1,x[i<<1],x[i<<1|1],1,cnt,1);
        printf("%d\n",query(1,cnt,1,cnt,1));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值