poj 2528 Mayor's posters

线段树(经典题)

离散化+成段更新+区间统计

题意:先输入case数,每个case输入n,表示下面有n个海报,每行就是海报的左右坐标,第i个海报的颜色为i。一面墙长度固定为10000000,问这些海报贴上去后能看到多少种颜色

这个问题的难处其实是怎么离散化(也就是映射,映射后就是简单的线段树整段更新,最后区间询问)。今天第一次学离散化,说说个人的解法

方法是和别人的大概一样的,可能代码实现有些不同,我的代码可能空间开销更大些(后来查看代码,很多空间能省略,但是下面的代码是我最原始的代码,并没有后来的优化,就以此来讲解)。

int s[MAXN][2];  保存原始的数据,[0]是起点坐标,[1]是终点坐标,那么一共产生2*n个端点

struct point
{
int a,n,f; //端点坐标,属于哪条线段,起点或者终点
}p[2*MAXN];

所以把2*n个端点逐一放入p数组中,并且要记录这个端点是来自哪条线段(n这个域),在这条线段中是起点还是终点(f这个域)

然后对p数组排序,以端点坐标大小排序(a这个域)

 

接下来是映射,例如排序后的结果为

10,21,38,40,40,59

映射为

1,2,3,4,4,5

也就是说按数字大小映射,而且也可以发现,最后的5其实也代表了有多少个不同的数字

这个映射的结果一定包含[1,m]的所有整数,等下我们要建线段树的时候,总区间的长度就是[1,m]

我RE了很多次,就是搞错了m的大小范围,m最大可以去到80000(60000也行),80000怎么来的,是2*10000*4

因为海报的个数最多10000,有20000个端点,极端情况下,这20000个点都不一样,映射过去的话,就是[1,20000],也就是m最大可以是m

回想一般情况下,线段树的总区间为[1,m]的话,我们开辟线段树的数组一般为4*m(其实开到3*m就足够了),所以这就是为什么80000的原因

 

说回头,我们扫过p数组的时候,没得到一个点,就先映射它的坐标(和它前面那个端点坐标比较是否不同,不同的话,就是一个新的点,具体看代码),然后看它是来自哪条线段(映射后同样是这条线段),再看它在原线段中是起点还是终点,然后相对应地记录现在线段的起点和终点

 

最后映射后的线段信息全部在下面的数组中

struct interval //离散化后的线段
{
int l,r,col;
}in[MAXN];

 

得到了它,就可以建树,整段更新,查询了

 

/*

处理的主要问题就是如何映射,映射结束后,就是整段区间更新,最后询问总区间有多少种不同的颜色

*/



#include <cstdio>

#include <cstring>

#include <algorithm>

#define MAXN 10010



using namespace std;



bool used[MAXN]; //询问时记录哪些颜色已经被计数

int s[MAXN][2] , N; //s数组记录原始数据,不过可以省略这个数组

struct point 

{

    int a,n,f; //端点坐标,属于哪条线段,起点或者终点

}p[2*MAXN];

struct interval //离散化后的线段

{

    int l,r,col;

}in[MAXN];

struct segment //线段树结点

{

    int l,r,col,val;

}t[8*MAXN];



int query(int a ,int b ,int rt)

{

    int col=t[rt].col;

    if(t[rt].val) //单色

    {

        if(!used[col]) //此颜色没被用过

        { 

            used[col]=1;

            return 1; 

        }

        else  //已经被用过

            return 0;

    }

    int mid=(t[rt].l+t[rt].r)>>1;

    /*

    if(a>mid) //右孩子

        return query(a,b,rt<<1|1);

    else if(b<=mid) //左孩子

        return query(a,b,rt<<1);

    else //左右孩子

    */

        return query(a,mid,rt<<1)+query(mid+1,b,rt<<1|1);

}



void updata(int a ,int b ,int col ,int rt)

{

    if(t[rt].val && t[rt].col==col) return ;

    //小剪枝,当前区间单色且与要更改的区间颜色相同则返回不用深入

    if(t[rt].l==a && t[rt].r==b) //目标区间

    {

        t[rt].col=col; t[rt].val=1;  //单色

        return ;

    }

    if(t[rt].val) //单色,传递给左右孩子

    {

        t[rt<<1].val=t[rt<<1|1].val=t[rt].val;

        t[rt<<1].col=t[rt<<1|1].col=t[rt].col;

        t[rt].val=0; //修改为不是单色

    }

    int mid=(t[rt].l+t[rt].r)>>1;

    if(a>mid) //访问右孩子

        updata(a,b,col,rt<<1|1);

    else if(b<=mid) //访问左孩子

        updata(a,b,col,rt<<1);

    else //左右均访问

    {

        updata(a,mid,col,rt<<1);

        updata(mid+1,b,col,rt<<1|1);

    }

}



void build(int a ,int b ,int rt)

{

    t[rt].l=a; t[rt].r=b; t[rt].val=0; t[rt].col=0; //不是单色的

    if(a==b) return ;

    int mid=(a+b)>>1;

    build(a,mid,rt<<1);

    build(mid+1,b,rt<<1|1);

}



int cmp(struct point x ,struct point y)

{

    return x.a<y.a?1:0;

}



int main()

{

    int Case;

    scanf("%d",&Case);

    while(Case--)

    {

        scanf("%d",&N);

        for(int i=1; i<=N; i++) 

        {

            scanf("%d%d",&s[i][0],&s[i][1]);

            p[2*i-1].a=s[i][0]; p[2*i-1].n=i; p[2*i-1].f=0;

            p[2*i].a=s[i][1];   p[2*i].n=i;   p[2*i].f=1;

        }

        sort(p+1,p+2*N+1,cmp);

        p[0].a=p[1].a;  //为了下面的循环要设一个特殊值

        int m=1 , n ,f;  //表示有多少个不同的数字,也就是映射使用的

        for(int i=1; i<=2*N; i++)

        {

            n=p[i].n , f=p[i].f;

            if(p[i].a!=p[i-1].a) m++;

            if(!f) //起点

            { in[n].l=m; in[n].col=n; }

            else  //终点

            { in[n].r=m; in[n].col=n; }

        }

        //for(int i=1; i<=N; i++) printf("[%d , %d] %d\n",in[i].l,in[i].r,in[i].col);

        /*

        至此,离散化已经结束

        接下来就是建树

        然后利用已经保存好的区间去整段更新

        最后加上一个查询即可

        */

        build(1,m,1);  //整个线段树的区间长度[1,m]

        for(int i=1; i<=N; i++)

            updata(in[i].l, in[i].r, in[i].col, 1);

        memset(used,0,sizeof(used));

        printf("%d\n",query(1,m,1));

    }

    return 0;

}

 

你可能感兴趣的:(post)