POJ 2528 线段树离散化

【题目链接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=14608

[解题报告】
题目大意:
给定长度区间(L,R)//L,R<=1e7,
给出N个区间操作,把li-ri区间贴上海报 //N《=1e5
求:有多少张海报没有被完全覆盖

这道题目的关键在于,直接开1e7的线段树一定会MLE,但我们观察到一共只有不超过1e5张海报,如果每张海报我们只取它的两个端点,只对它的端点进行处理,空间复杂度就会大大降低。
所以如何处理呢?
比如区间[1,4],[5,6]有四个不同端点,说明我们需要把他映射到线段树上四个不同的位置。
我们从1开始对线段树的叶子节点进行标记,那么映射到的相对应的线段树区间为:
[1,4]->[1,2]
[5,6]->[3,4]
这时候出现一个问题:
我们先后贴上[1,10],[1,4],[5,10],映射为:[1,4],[1,2],[3,4]只能看见两张海报
如果先后贴上[1,10],[1,4],[7,10],映射为:[1,4],[1,2],[3,4],只能看见两张海报。
哪里出了问题?
问题在于:
如果两条相邻线段并不相交,离散化之后,它变成了相交!
所以维护这样的性质:如果两个顶点并不紧邻,那么我们离散化之后,它们仍然不能紧邻。方法是在两个顶点之间再插入一个顶点。

到了这里,大部分问题就解决了。
还需要思考一个问题:如何查找没有被完全覆盖的海报的数目。
容易想到,我们更新的时候,给区间(l,r)打上标记i表示当前位置的海报是i,这样最后统计有多少个不同的i即可。
为了不重复查询相同的,没有被完全覆盖的i,如: (1,10),( 3,4 ),(7,8)更新之后的标记就是1,2,1,3,1,会出现多个第1张海报的标记,我们需要单独设置一个hash数组,如果某张海报已经被统计过了,就不再重复统计。

这样,剩下的就是线段树模板的内容了。
剩下的细节可以参考代码。我的线段树风格参考刘汝佳的《训练指南》。

【参考代码】

#include
#include
#include
#include
using namespace std;

const int maxn=1e5+50;

int N,m,cnt;
int l[maxn],r[maxn],vis[maxn],X[maxn];
int cover[maxn*4];


void uniq( int nn )
{
    m=0;
    X[++m]=X[1];
    for( int i=2; i<=nn; i++ )
    {
        if( X[i]!=X[i-1] )X[++m]=X[i];
    }
    sort( X+1,X+1+m );
    int t=m;
    for( int i=1;iif( X[i]!=X[i+1]-1 )X[++m]=X[i]+1;
    }
    sort( X+1,X+1+m );
}

void build( int O, int L, int R )
{
    if(L==R)cover[O]=-1;
    else
    {
        int mid=(L+R)/2;
        build(O*2+1,mid+1,R );
        build( O*2,L,mid );
        cover[O]=-1;
    }
}

void pushdown( int O )
{
    if( cover[O]!=-1 )
    {
        cover[O*2]=cover[O*2+1]=cover[O];
        cover[O]=-1;
    }
}

void update( int O, int L, int R, int qL, int qR, int c )
{
    if( qL<=L && R<=qR )
    {
        cover[O]=c;
    }
    else
    {
        pushdown( O );
        int mid=(L+R)/2;
        if(  qL<=mid )update( O*2,L,mid, qL,qR,c );
        if( qR>mid )update( O*2+1, mid+1,R,qL,qR,c );
    }
}


void query( int O, int L, int R )
{
    if( L==R )
    {
        if( cover[O]!=-1 && !vis[cover[O]] )
        {
            vis[cover[O]]=1;
            cnt++;
        }
    }
    else
    {
        pushdown(O);
        int mid=(L+R)/2;
        query( O*2, L,mid );
        query( O*2+1,mid+1,R );
    }
}

int main()
{
    //freopen("2528","r",stdin);
    int T; cin>>T;
    while(T--)
    {
        scanf( "%d",&N );
        int nn=0;
        for(  int i=1; i<=N; i++ )
        {
             scanf( "%d%d",&l[i],&r[i] );
             X[++nn]=l[i]; X[++nn]=r[i];
        }
        sort( X+1,X+1+nn );
        uniq(nn);
        build( 1,1,m );
        for( int i=1; i<=N; i++ )
        {
            int left=lower_bound( X+1,X+1+m,l[i] )-X;
            int right=lower_bound( X+1,X+1+m,r[i] )-X;
            update( 1,1,m,left,right,i );
        }
        memset( vis,0,sizeof vis );
        cnt=0;
        query( 1,1,m );
        printf( "%d\n",cnt );
    }

    return 0;
}

你可能感兴趣的:(数据结构-线段树)