POJ 2528 Mayor's posters ( 线段树 : 区间修改 + 离散化 )

来源:互联网 发布:淘宝客服的上班时间 编辑:程序博客网 时间:2024/06/05 06:10

原题网址: http://poj.org/problem?id=2528

Mayor’s posters
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 57855 Accepted: 16731

Description
The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules:

Every candidate can place exactly one poster on the wall.
All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown).
The wall is divided into segments and the width of each segment is one byte.
Each poster must completely cover a contiguous number of wall segments.

They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections.
Your task is to find the number of visible posters when all the posters are placed given the information about posters’ size, their place and order of placement on the electoral wall.

Input
The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers li and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= li <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered li, li+1 ,… , ri.

Output
For each input data set print the number of visible posters after all the posters are placed.

The picture below illustrates the case of the sample input.

Sample Input

1
5
1 4
2 6
8 10
3 4
7 10

Sample Output

4

样例

题意:

好多人在一个线段上贴海报,后贴的海报覆盖以前贴的,问最后能看见多少人的海报。

离散化:

离散化,把无限空间中无限的个体映射到有限的空间中去,以此提高算法的时空效率。—百度百科
在此题中离散化就是构造一个映射关系,使得在不影响正确性的情况下,对数据进行压缩。可以结合题意理解。

题目中最大的点是1000万,数字太大了,开线段树的数组肯定(大概)开不下,所以就把点与点做一个映射关系,排除那些用不到的点。也就减少了空间复杂度。
比如: 1-10000 1-4 6-2000 这三个线段
先按照点排序:

排序 1 1 4 6 2000 10000 映射 1 1 2 3 4 5

但是你会发现这里有问题,映射之前的算出来应该是3,映射后的 1-5 1-2 3-5 结果是2
所以在 4和2000 这种间隔大于1的数做映射时应该假装中间还有个数。如图:

1 (2) 4 (5) 6 (7) 2000 (2001) 10000 映射 1 2 3 4 5 6 7 8 9

这样离散化的正确性就得到了保证。

关于线段树

区间修改涉及到线段树的懒操作,这是个小难点,用一句话解释就是,不到必要的时候,我绝不用我的值更新我的孩子(这样就极大的减少了不必要的操作),在这里,col数组就相当于一个懒的标志。

ps:因为离散化的原因,导致点的个数变了,题目中是20000个点,但是由于我们离散化要加一些没(有)用的点,实际上点数可能多达四万。所以N取了个4w。

pss;线段树一般开4*N的大小,之前也是不太理解。现在稍稍懂了一点,N是最下层的子叶的节点数,它的上一层的点的个数是N/2 ,再上一层为N/4 ,所以总数为 N*(1 + 1/2 + 1/4 + 1/8 ……)等比数列求和之后得2*N。再来考虑有可能出现更下一层的子叶节点,所以再乘2(这句话是我学长说的,我其实也不是很理解,但大家都开4倍,我也开不就得了~

吐槽:之前把N开成2w+了,导致我的update函数在更新线段树的时候越界了,然后,并没有报错,
因为内存的连续分配,程序直接把我开的其他的数组当作col数组用了,然后瞎xx更改我其他数组里的数据,导致二分的时候找不到对应的位置,坑爹呢,我特意debug发现经过sort的a数组竟然不是有序的就是因为这个了。

ac代码如下:

#include <iostream>#include <cstdio>#include <math.h>#include <vector>#include <algorithm>#include <string.h>#define lson l, m, rt<<1#define rson m+1, r, rt<<1|1#define mid (l+r)>>1#define N 40100using namespace std;int col[N*4];int le[10020],ri[10020];int a[20050];vector <int> b;void Pushdown(int rt){    col[rt<<1] = col[rt<<1|1] = col[rt];    col[rt] = -1;}//线段树功能:update:区间染色void update(int L,int R,int c,int l,int r,int rt){ // L,R 是要贴的区间    if( L <= l && r<= R){ // 如果要贴的区间大于等于现在我的根所在的区间,更新。        col[rt] = c; // 染成 c 的颜色        return;    }    if(col[rt] != -1)Pushdown(rt);    // 如果此段区间已经被染色,那么把染的色传递到他的子区间,    //使得接下来进行的更新的区间的默认值正确    int m = (l+r) >> 1;    if(L <= m)update(L,R,c,lson);    if(R > m) update(L,R,c,rson);}int ans = 0; // answerbool has[20020];//has[i] 表示颜色为i 的是否已经记录过。初始化为 falsevoid query(int l,int r,int rt) {    if(col[rt] != -1){ // 此区间已经染好了,判断这个颜色是否记录过了。        if(!has[col[rt]]){            ans++;            has[col[rt]] = true;        }        return;    }    if(l == r)return; // 搜到最底层了,结束。    int m = (l+r) >> 1;    query(lson);    query(rson);}int binary(int key,int n){ // 二分搜索,离散化之后寻找很大的数对应的值。n 表示点的数量。    int l = 0,r = n-1;    while(l <= r){        int m = (l+r) >> 1;        if(b[m] == key)return m+1 ;        // 这里 +1 是因为 我用来离散化的vector是从零开始的,        //也就是说我从0开始跟那些大数对应的,所以+1        if(b[m] < key){            l = m+1 ;        }        else r = m-1 ;    }    return -1;    // 讲道理其实这个-1没啥用,因为我的key值是肯定可以找到的(毕竟是我把这个值放到vector里去的)}int main() {    int T , n;    scanf("%d",&T);    for (int cas = 1 ; cas <= T ; cas ++) {        int cnt = 0;        scanf("%d",&n);        for(int i = 0;i < n;i++){            scanf("%d%d",le+i,ri+i);            a[cnt++] = le[i];            a[cnt++] = ri[i];        }        // 离散化        sort(a,a+cnt);        b.push_back(a[0]);        for(int i = 1;i < cnt;i++){            if( a[i] != a[i-1] ){                if( a[i] != a[i-1]+1 ){                    b.push_back( a[i-1]+1 ); // 没什么实际作用,只是占个位置,让离散化正确。                    b.push_back( a[i] );                }                else{                    b.push_back(a[i]);                }            }        }        int m = b.size();        memset(col,-1,sizeof(col));        for(int i = 0;i < n;i++){ // 最多n条线段            int ll = binary(le[i],m);// 二分查找对应的点。            int rr = binary(ri[i],m);            update(ll,rr,i,1,m,1);        }        memset(has,false,sizeof(has));        query(1,m,1);        cout<<ans<<endl;        ans = 0;        b.clear();    }    return 0;}
2 1
原创粉丝点击