poj 2528 Mayor's posters

Mayor's posters
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 60796 Accepted: 17591

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 l i 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 <= l i <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered l i, l i+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

Source

提示

解析来源于书。

题意:

市长竞选,每个市长都往墙上贴海报,海报之间彼此可以覆盖,给出粘贴顺序和每个海报的起点和长度,问最后有多少海报是可见的。

思路:
这是一道经典的线段树题目,另外加上离散化的方法。离散化:由于题目中wall最大为10000000(10^7),直接线段树无疑会大大超出内存限制。所以要对其离散化,基本做法是:先对所有端点坐标进行排序,用相应序号代替端点坐标构造线段树进行计算。这样最大的序号也只是2*n。在构造线段树的结点结构体时,添加变量kind。kind=0时表示kind线段没有贴海报,或者贴了不止一张海报;kind>0时表示贴了一张海报,并且kind的值表示贴的是第几张海报。线段树更新的基本原理就是,贴海报i时,如果遇到某一线段区间能够被当前海报完全覆盖,那么更新到此区间,即让它的变量kind更新为海报i,记录下i的值。如果不能被海报完全覆盖,则只是这一线段区间的部分区间需要修改,则更改这一区间的子区间即可。
书中的离散化有漏洞,这里给出一个比较准确的:http://blog.csdn.net/non_cease/article/details/7383736
每个数表示的是区间而不是点,题目的数据比较水,比如讨论区里的一组数据:

3
5 6
4 5
6 8
书上输出的是:2
改过的为:3
正确答案为:3

示例程序

书上的做法:

Source Code

Problem: 2528		Code Length: 2488B
Memory: 1588K		Time: 94MS
Language: C++		Result: Accepted
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define N 10005
int result;
int l[N],r[N];
bool mark[N];				//在最后统计时用到,统计过的节点标为true
struct line
{
    int kind,left,right;			//线段被覆盖的种类
}lines[10*N];
struct item
{
    int coord,id;
}poster[2*N];
void build(int s,int t,int node)
{
    lines[node].left=s;
    lines[node].right=t;
    if(s==t)
    {
        return;
    }
    int mid=(lines[node].left+lines[node].right)/2;
    build(s,mid,node*2);
    build(mid+1,t,node*2+1);
}
void update(int s,int t,int node,int cover)
{
    if(lines[node].left==s&&t==lines[node].right)
    {
        lines[node].kind=cover;		//若结点node与插入线段完全重合,只需将其覆盖种类更改为cover代表的种类
        return;
    }
    /*若结点node已经被覆盖,且覆盖类型!=cover,需先将node内部范围更改成node的类型,然后node本身变成0.
    接下来在具体处理s-t的覆盖情况*/
    if(lines[node].kind!=0&&lines[node].kind!=cover)
    {
        lines[node*2].kind=lines[node].kind;
        lines[node*2+1].kind=lines[node].kind;
        lines[node].kind=0;
    }
    int mid=(lines[node].left+lines[node].right)/2;
    if(t<=mid)
    {
        update(s,t,node*2,cover);			//更新左子树
    }
    else if(s>mid)
    {
        update(s,t,node*2+1,cover);			//更新右子树
    }
    else
    {
        update(s,mid,node*2,cover);		//这是left<s<mid<t<right的情况,左右子树均有一部分需要更新
        update(mid+1,t,node*2+1,cover);
    }
}
void cal(int node)
{
    if(lines[node].kind!=0)				//表明未被覆盖
    {
        if(mark[lines[node].kind]==false)			//表明还未被统计过
        {
            mark[lines[node].kind]=true;			//标记为已统计
            result++;
        }
    }
    else
    {
        cal(2*node);
        cal(2*node+1);
    }
}
int cmp(const void *p,const void *q)
{
    return ((item*)p)->coord-((item*)q)->coord;
}
int main()
{
    int t,n,i,j;
    struct item *templ,*tempr,tl,tr;
    scanf("%d",&t);
    while(t--)
    {
        memset(lines,0,sizeof(lines));
        memset(poster,0,sizeof(poster));
        memset(mark,false,sizeof(mark));
        scanf("%d",&n);
        for(i=j=1;n>=i;i++)
        {
            scanf("%d %d",&l[i],&r[i]);
            poster[j++].coord=l[i];			//把每一幅海报的坐标都记录下来,不分左右,以便离散化
            poster[j++].coord=r[i];
        }
        qsort(poster+1,n*2,sizeof(item),cmp);
        for(i=j=1;n*2>=i;i++,j++)
        {
            poster[j].coord=poster[i].coord;
            poster[j].id=j;				//每个坐标绑定一个编号
            while(poster[i].coord==poster[i+1].coord)		//删除重复的坐标,因为相同的坐标只需要编一个号码即可
            {
                i++;
            }
        }
        build(1,j-1,1);				//建树
        for(i=1;n>=i;i++)
        {
            tl.coord=l[i];			//tl是起始坐标,tr是终点坐标,二者是同一条线段的两个端点
            tr.coord=r[i];
            templ=(item*)bsearch(&tl,poster+1,j,sizeof(item),cmp);		//二分找出tl的地址,以求出其编号
            tempr=(item*)bsearch(&tr,poster+1,j,sizeof(item),cmp);		//二分找出tr的地址,以求出其编号
            update(templ->id,tempr->id,1,i);			//插入两个编号构成的线段,更新
        }
        result=0;
        cal(1);					//统计,根节点是1
        printf("%d\n",result);
    }
    return 0;
}
自己的代码(参考网址中和书上的改了一下):
Source Code

Problem: 2528		Code Length: 2625B
Memory: 2216K		Time: 79MS
Language: G++		Result: Accepted
#include <cstdio>
#include <algorithm>
using namespace std;
struct line
{
    int l,r,kind;
}poster[160000];
int pointl[10000],pointr[10000],point[40000],v[10000],num;
void bulid(int t,int l,int r)
{
    int mid;
    poster[t-1].l=l;
    poster[t-1].r=r;
    poster[t-1].kind=-1;
    if(l==r)
    {
        return;
    }
    mid=(l+r)/2;
    bulid(t*2,l,mid);
    bulid(t*2+1,mid+1,r);
}
void update(int t,int l,int r,int color)
{
    int mid;
    if(poster[t-1].l>=l&&poster[t-1].r<=r)			//这里做了延迟更新,就是没有对子节点进行更新
    {
        poster[t-1].kind=color;
        return;
    }
    if(poster[t-1].kind!=-1&&poster[t-1].kind!=color)
    {
        poster[t*2-1].kind=poster[t-1].kind;
        poster[t*2].kind=poster[t-1].kind;
        poster[t-1].kind=-1;
    }
    mid=(poster[t-1].l+poster[t-1].r)/2;
    if(r<=mid)
    {
        update(t*2,l,r,color);
    }
    else if(l>mid)
    {
        update(t*2+1,l,r,color);
    }
    else
    {
        update(t*2,l,mid,color);
        update(t*2+1,mid+1,r,color);
    }
}
int find(int l,int r,int x)
{
    int mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(point[mid]==x)
        {
            return mid;
        }
        else if(point[mid]>x)
        {
            r=mid-1;
        }
        else
        {
            l=mid+1;
        }
    }
}
void query(int t,int l,int r)
{
    int mid;
    if(poster[t-1].kind!=-1)
    {
        if(v[poster[t-1].kind]==0)
        {
            v[poster[t-1].kind]=1;
            num++;
        }
        return;
    }
    if(l==r)
    {
        return;
    }
    mid=(l+r)/2;
    query(t*2,l,mid);
    query(t*2+1,mid+1,r);
}
int main()
{
    int t,n,i,i1,top;
    scanf("%d",&t);
    for(i=1;t>=i;i++)
    {
        scanf("%d",&n);
        top=0;
        num=0;
        for(i1=0;n>i1;i1++)
        {
            scanf("%d %d",&pointl[i1],&pointr[i1]);
            point[top]=pointl[i1];
            top++;
            point[top]=pointr[i1];
            top++;
            v[i1]=0;
        }
        sort(point,point+top);
        top=1;
        for(i1=1;n*2>i1;i1++)
        {
            if(point[i1]!=point[i1-1])
            {
                point[top]=point[i1];
                top++;
            }
        }
        for(i1=top-1;i1>=1;i1--)			//这里增加了几个点,防止漏算
        {
            if(point[i1]-point[i1-1]>1)
            {
                point[top]=point[i1]-1;
                top++;
            }
        }
        sort(point,point+top);
        bulid(1,0,top-1);
        for(i1=0;n>i1;i1++)
        {
            update(1,find(0,top-1,pointl[i1]),find(0,top-1,pointr[i1]),i1);
        }
        query(1,0,top-1);
        printf("%d\n",num);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值