• POJ 2528 Mayor's posters (线段树,染色问题,离散化要注意)


    做这题建议看一下该题的discuss。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <set>
    /*
    
    题意:给出n张海报的左端点和右端点,按顺序往墙上贴。问最后整张墙能看到几张海报。
    
    注意:按题意是如此够造树的,即每个点其实是一小块段。
    |___|___|___|___|___|___|___|___|
      1   2   3   4   5   6   7   8
    
    思路:相当于染色问题,这里我每个节点有个color值。
        color值有三种情况:1.0表示该区间为染色;2.-1表示该区间含有多种颜色;3.正整数表示该区间全是一种颜色
        每次更新(即第i次“涂色”)的时候,如果区间在范围内,则该区间color值替换为此次的颜色(用i表示)。
        最后查询的时候,从根节点往下查,直到查询到color值为某一个正整数,标记该颜色对应的序号。
        最后统计有多少个标记,即为答案。
    
    由于数据大,要离散化,这才是重点!!!
    有些人离散化,直接按顺序相邻,即每次都只加1。这是错误的!!!
    给个数据就明白了:
    1
    3
    1 10
    1 3
    6 10
    正确答案:3
    错误答案:2
    忽略了顺序相邻但位置不相邻的情况
    如果直接按顺序离散:
    1 4
    1 2
    3 4
    那么1 2和3 4的就会覆盖掉1 4,得出错误结果2
    即离散的时候3和6之间应该还要留出一段,即总共离散为5段,不然会把中间夹的给覆盖掉了
    
    所以离散的时候,排好序后,如果两个相邻的仅相差1,那么对应的映射+1即可。
    但如果相差大于1,则对应的映射+2,留出个空隙。
    
    在discuss里会有人说,如果按这正确的方法做,提交会WA;全部改成+1,才AC。
    其实+2导致WA的原因,是数组开的不够。
    我原本开的是tree[maxn<<3]的大小,用正确做法做,WA;但开大后,tree[maxn<<4],就AC了。
    也就是说,POJ上其实没有这类数据
    
    */
    using namespace std;
    const int maxn=10005;
    int n,m,idx;
    int maxh; //对应映射的最大值
    int hash_val[10000005]; //对应的映射
    int val[maxn*2]; //存储端点的值
    int vis[maxn]; //用来标记颜色
    //存储海报的左端点、右端点
    struct Pos{
        int left,right;
    }pos[maxn];
    
    struct Node{
        int color; //0:该区间没有染色;-1:该区间含多种颜色;正整数:该区间涂满一种颜色。
        bool lazy;
    }tree[maxn<<4];  //10000个海报,每个海报两个端点,所以最多会有20000个点,但开4倍不够,要开8倍,这样+2的方法才能AC
    
    void build(int rt,int L,int R){
        tree[rt].color=0;
        tree[rt].lazy=false;
        if(L==R)
            return;
        int mid=(L+R)>>1;
        build(rt<<1,L,mid);
        build(rt<<1|1,mid+1,R);
    }
    
    void pushUp(int rt){
        int lson=rt<<1,rson=rt<<1|1;
        //只要有一个子区间上面含多种颜色(color值为-1),该节点color值为-1
        if(tree[lson].color==-1 || tree[rson].color==-1)
            tree[rt].color=-1;
        //两个子区间都没涂有颜色
        else if(!tree[lson].color && !tree[rson].color)
            tree[rt].color=0;
        //两个子区间各自都涂有一种颜色
        else if(tree[lson].color && tree[rson].color){
            if(tree[lson].color==tree[rson].color)
                tree[rt].color=tree[lson].color;
            else
                tree[rt].color=-1;
        }
        else{
            //一个为0(即该区间没有染色),另一个为正数
            tree[rt].color=tree[lson].color|tree[rson].color;
        }
    }
    void pushDown(int rt){
        if(tree[rt].lazy){
            int lson=rt<<1,rson=rt<<1|1;
            tree[lson].color=tree[rson].color=tree[rt].color;
            tree[lson].lazy=tree[rson].lazy=true;
            tree[rt].lazy=false;
        }
    }
    void update(int rt,int l,int r,int L,int R,int color){
        if(l<=L && R<=r){
            tree[rt].lazy=true;
            tree[rt].color=color;
            return;
        }
        pushDown(rt);
        int mid=(L+R)>>1;
        if(r<=mid)
            update(rt<<1,l,r,L,mid,color);
        else if(l>mid)
            update(rt<<1|1,l,r,mid+1,R,color);
        else{
            update(rt<<1,l,mid,L,mid,color);
            update(rt<<1|1,mid+1,r,mid+1,R,color);
        }
        pushUp(rt);
    }
    void query(int rt,int L,int R){
        //下面两种情况已经包含了叶子节点,因为叶子节点的color只能为0或者>0,不可能为-1。所以不会RE
        if(tree[rt].color>0){
            //如果该区间涂有一种颜色,则标记该颜色
            vis[tree[rt].color]=1;
            return;
        }
        //如果区间没有染色,直接return。
        if(!tree[rt].color){
            return;
        }
        int mid=(L+R)>>1;
        query(rt<<1,L,mid);
        query(rt<<1|1,mid+1,R);
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            idx=0;
            for(int i=1;i<=n;i++){
                scanf("%d%d",&pos[i].left,&pos[i].right);
                val[idx++]=pos[i].left;
                val[idx++]=pos[i].right;
            }
            sort(val,val+idx);
            hash_val[val[0]]=1;
            //离散
            for(int i=1;i<idx;i++){
                if(val[i]==val[i-1])
                    hash_val[val[i]]=hash_val[val[i-1]];
                //如果两个端点不相邻,则+2
                else if(val[i]-val[i-1]>1)
                    hash_val[val[i]]=hash_val[val[i-1]]+2;
    
                //如果两个端点相邻,则+1.
                else{
                    hash_val[val[i]]=hash_val[val[i-1]]+1;
                }
                maxh=hash_val[val[i]];
            }
            build(1,1,maxh);
            for(int i=1;i<=n;i++){
                update(1,hash_val[pos[i].left],hash_val[pos[i].right],1,maxh,i);
            }
            memset(vis,0,sizeof(vis));
            query(1,1,maxh);
            int ans=0;
            for(int i=1;i<=n;i++){
                if(vis[i]){
                    ans++;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    关于掌握C#的内存堆栈概念
    sqlserver 跨服务器查询
    写单元测试的知识结构(2)——单元测试工具的选用(找个顺手的)
    写单元测试的知识结构(1) —— 单元测试用处
    如何能保证自己的功能代码不出纰漏
    Mysql查询架构信息
    Mysql 分组查询最高分
    Mysql按照字段值做分组行转列查询
    软件测试学习笔记:主路径测试
    软件测试学习笔记:Junit入门
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3389410.html
Copyright © 2020-2023  润新知