POJ 2528 Mayor's posters

题意:

在一个墙上贴海报,给你每次贴海报的左边起点,和右边的终点,如果有重叠的部分,后贴的会覆盖前面贴上去的那部分。问最后从正面上看,能看到多少张。

思路:

我们可以把整面墙看成一条线段,每次贴海报都是对线段的一次染色,每次染不同的色。最后查找有多少种颜色在这条线段上,这个问题就解决了。

首先需要解决数据的问题,由于本题的数据范围很大,但是我们注意到,他的线段个数很少,所以我们利用这一点,对数据进行离散化。

离散化:

1.先把所有的数都放到一个数组里面。

2.对这个数组进行排序。

3.然后依次对每个数编号

4.对数组的值和下标作一个映射。

但是由于离散化的是一个区间,所以如果按照上面正常的离散化就会出现问题:

1 10

1 4

6 10

这样我们离散化出来的是:1 4 6 10

   0 1 2  3

在进行操作的时候,操作的是 0 - 3、0 - 1、2 - 3,这样看上去就会是填满了整条线段,但实际上4 - 6 中间会有一个点被忽略了。

所以,我们在上面的操作2后再加一步,在两个不相邻数的中间加一个数,来使数据的表达完整。

刚才的例子:就可以变成 1 2 4 5 6 10

 0 1 2 3 4 5

这样就能做到无遗漏。

其余的就是线段树插线操作了,还是延时标记。

Code:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<map>
#include<cctype>
#include<vector>

//#define TEST

#define LL long long
#define Mt(f, x) memset(f, x, sizeof(f));
#define rep(i, s, e) for(int i = (s); i <= (e); ++i)
#ifdef TEST
    #define See(a) cout << #a << " = " << a << endl;
    #define See2(a, b) cout << #a << " = " << a << ' ' << #b << " = " << b << endl;
    #define debug(a, s, e){ rep(_i, s, e) {cout << a[_i] << ' '; }cout << endl;}
    #define debug2(a, s, e, ss, ee) rep(i_, s, e) {debug(a[i_], ss, ee);}
#else
    #define See(a)
    #define See2(a, b)
    #define debug(a, s, e)
    #define debug2(a, s, e, ss, ee)
#endif

const int MAX = 2e9;
const int MIN = -2e9;
const int PI = acos(-1.0);
const double eps = 1e-8;

using namespace std;

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const int N = 20000 + 5;

int f[N << 4];
int l[N], r[N];
int a[N << 3];
bool v[N << 3];
int ans;

void pushDown(int rt)
{
    if(f[rt])
    {
        f[rt << 1] = f[rt << 1 | 1] = f[rt];
        f[rt] = 0;
    }
}

void update(int L, int R, int x, int l, int r, int rt)
{
    if(l >= L && r <= R)
    {
        f[rt] = x;
        return ;
    }
    pushDown(rt);
    int m = (l + r) >> 1;
    if(L <= m) update(L, R, x, lson);
    if(R > m) update(L, R, x, rson);
}

void pushAll(int l, int r, int rt)
{
    if(f[rt])
    {
        if(!v[f[rt]])
        {
            ++ans;
            v[f[rt]] = true;
        }
    }
    if(l == r)
    {
        return ;
    }
    pushDown(rt);

    int m = (l + r) >> 1;
    pushAll(lson);
    pushAll(rson);
}

int mp(int key, int n, int a[])
{
    return upper_bound(a, a + n, key) - a;
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        Mt(v, false);
        Mt(f, 0);
        int n;
        scanf("%d", &n);
        int num = 0;
        for(int i = 0; i < n; ++i)
        {
            scanf("%d%d", &l[i], &r[i]);
            a[num++] = l[i];
            a[num++] = r[i];
        }
        sort(a, a + num);
        num = unique(a, a + num) - a;
        int newNum = num;
        for(int i = 1; i < num; ++i)
        {
            if(a[i] != a[i - 1] + 1)
            {
                a[newNum++] = a[i - 1] + 1;
            }
        }
        num = newNum;
        sort(a, a + num);
        debug(a, 0, num - 1);
        for(int i = 0; i < n; ++i)
        {
            update(mp(l[i], num, a), mp(r[i], num, a), i + 1, 0, num, 1);
        }
        ans = 0;
        pushAll(0, num, 1);
        printf("%d\n", ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值