Poj 2528 Mayor's posters

题目链接:http://poj.org/problem?id=2528

线段树成段更新。这题关于线段树这个数据结构倒是没什么说的,关键是题意的理解和思路的整理。查询的时候,实际上是二分遍历整个查询区间,每一个单位用vis来标记是否访问过。然后用D[x]来识别是否整个区间是连续的。

先不考虑离散化的问题。关于Update,我觉得本身它的Pushdown操作中携带本身题目要求的覆盖的成分,所以Pushdown是必须的。而不需要Pushup。

针对于Query操作。网上很多的题解中都没有Pushdown,这是因为本身Update的某个区间肯定和Query的某个区间是相同的,万一不同,还是需要Pushdown的,所以我写了这个操作在Query里,也是能AC的,而且我觉得是必要的。

关于离散化,由于数据范围大,查询量少。所以离散化是必要的,没什么好说的。。但是有一点,很多人忽略了完全覆盖的问题,即便是AC代码也是这样,事实上,这是因为Poj这题的数据太弱了。。。

例子一:1-10 1-4 5-10
例子二:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖

为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,下标位置向后延一下就行了 。。。比如说6是排名第三位的,我把它排名到第四位就好了。这个原理应该不难想出来的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>

using namespace std;
#define Maxn 60005
#define lx (x<<1)
#define rx ((x<<1) | 1)
#define MID ((l + r)>>1)
struct Node
{
    int val;
    int id;
    bool operator <(const Node &a) const
    {
        return val<a.val || (val == a.val && id<a.id);
    }
}A[Maxn];
int D[Maxn<<2];
int vis[Maxn];
int rank[Maxn];
int t,n;
void init()
{
    memset(D,0,sizeof(D));
    memset(vis,0,sizeof(vis));
}
void pushDown(int x)
{
    if(D[x])
    {
        D[lx] = D[rx] = D[x];
        D[x] = 0;
    }
}
int query(int L,int R,int l,int r,int x)
{
    if(D[x])
    {
        if(!vis[D[x]])
        {
            vis[D[x]] = 1;
            return 1;
        }
        return 0;
    }
    //我觉得这一步是必要的。即使对于本题没有作用,但是思路上要求我们要这么做。
    pushDown(x);
    if(l == r) return 0;
    int ans = 0;
    if(L<=MID) ans += query(L,R,l,MID,lx);
    if(MID+1<=R) ans +=query(L,R,MID+1,r,rx);
    return ans;
}
void update(int d,int L,int R,int l,int r,int x)
{
    if(L<=l && r<=R)
    {
        D[x] = d;
        return;
    }
    pushDown(x);
    if(L<=MID) update(d,L,R,l,MID,lx);
    if(MID+1<=R) update(d,L,R,MID+1,r,rx);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    scanf(" %d",&t);
    while(t--)
    {
        scanf(" %d",&n);
        n *= 2;
        for(int i=0;i<n;i++)
        {
            scanf(" %d",&A[i].val);
            A[i].id = i;
        }
        sort(A,A+n);
        int cnt = 1;
        rank[A[0].id] = cnt;
        int lastValue = A[0].val;
        for(int i=1;i<n;i++)
        {
            if(A[i].val == lastValue) rank[A[i].id] = cnt;
            //相邻数据如果相隔大于1,Rank向后延一位
            else if(A[i].val - lastValue>1)
            {
                ++cnt;
                rank[A[i].id] = ++cnt;
                lastValue = A[i].val;
            }
            else
            {
                rank[A[i].id] = ++cnt;
                lastValue = A[i].val;
            }
        }
        init();
        for(int i=0;i<n/2;i++) update(i+1,rank[i<<1],rank[i<<1 | 1],1,cnt,1);
        int ans = query(1,cnt,1,cnt,1);
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值