POJ-2585-Mayor's posters-成段更新+(hash)离散化思想

题目链接:http://poj.org/problem?id=2528

这个题目是很经典的离散化成段更新线段树问题,可以试着多做几次,充分理解;

转自:http://www.notonlysuccess.com/index.php/segment-tree-complete/

题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报

思路:这题数据范围很大,直接搞超时+超内存,需要离散化:

离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了

所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多

而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)

给出下面两个简单的例子应该能体现普通离散化的缺陷:

1-10 1-4 5-10

1-10 1-4 6-10

为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]

如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.

线段树功能:update:成段替换 query:简单hash

好吧,现在这个代码只能过poj的数据,并不是真正的完美。。。不知为何一直RE,欢迎指点;

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
//  照着题目数组开10005,给报RE,坑啊;
int pos[100005][2];  //  用于离散化,存离散化后区间的左右节点;
bool vis[100005];
int ans,mm;         //  ans用于统计最后结构,mm表示第几张海报;
struct node1        //  用于离散化的结构体;
{
    int p,n;        //  p存节点,n存第几张海报;
}Line[100005<<1];
struct node2        //  线段树结构体;
{
    int l,r,c;
}node[100005<<2];
bool cmp(node1 x,node1 y)   //  按左节点递增排序;
{
    return x.p<y.p;
}
void build(int l,int r,int rt)  //  建树;
{
    node[rt].l=l;
    node[rt].r=r;
    node[rt].c=0;           //  染色标记;
    if(l!=r){
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
    }
}
void Insert(int l,int r,int c,int rt)   //  更新;
{
    if(node[rt].l==l&&node[rt].r==r){
        node[rt].c=c;
        return;
    }
    if(node[rt].c>0&&node[rt].c!=c){    //  说明得更新下一个节点,相当于我之前写的PushDown;
        node[rt<<1].c=node[rt].c;
        node[rt<<1|1].c=node[rt].c;
        node[rt].c=0;
    }
    int mid=(node[rt].l+node[rt].r)>>1;
    if(r<=mid) Insert(l,r,c,rt<<1);
    else if(l>mid) Insert(l,r,c,rt<<1|1);
    else{
        Insert(l,mid,c,rt<<1);
        Insert(mid+1,r,c,rt<<1|1);
    }
}
void query(int rt)                  //  区间查询;
{
    if(node[rt].c!=0){
        if(!vis[node[rt].c]){       //  统计被染了多少种不同的颜色;
            ans++;
            vis[node[rt].c]=true;
        }
        return;
    }
    query(rt<<1);
    query(rt<<1|1);
}
int main()
{
    int t,n,a,b;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d%d",&a,&b);    //  将a,b区间的长度离散成更小的区间
            Line[i<<1].p=a;
            Line[i<<1|1].p=b;
            Line[i<<1].n=-(i+1);    //  用负数表示左节点;
            Line[i<<1|1].n=i+1;     //  用正数表示右节点;
        }
        sort(Line,Line+2*n,cmp);    //  排序;
        int temp=Line[0].p; mm=1;
        for(int i=0;i<2*n;i++){
            if(Line[i].p!=temp){
                mm++;
                temp=Line[i].p;
            }
            //if(i>0&&Line[i].p>(Line[i-1].p+1)) mm++;   这条语句其实是我想解决离散化bug的,可是结果却一直RE,导致只过了POJ的较弱数据;
            if(Line[i].n<0) pos[-Line[i].n-1][0]=mm;
            else pos[Line[i].n-1][1]=mm;
        }
        build(1,mm,1);
        for(int i=0;i<n;i++){
            Insert(pos[i][0],pos[i][1],i+1,1);
        }
        memset(vis,false,sizeof(vis));
        ans=0;
        query(1);
        printf("%d\n",ans);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

POJ-2585-Mayor's posters-成段更新+(hash)离散化思想

时间: 2024-08-25 16:19:27

POJ-2585-Mayor's posters-成段更新+(hash)离散化思想的相关文章

Mayor&#39;s posters(线段树之点的成段更新加离散化)

bin神的萌萌哒专题 这道题目也是简单区间更新的线段树题目,不过题目的数据范围很大,直接搞,时间空间的花费都会异常的高,所以就要用到离散化来优化时间空间复杂度. 何为离散化?........................ 简单地说就是对于给出的庞大数据进行一种数据上的缩小. 比如给你一段(1,10000)的区间,由于我们要的不是其区间长度,我们只需要知道这段区间的状态 如何,于是我们可以忽视其长度,把其表示成(1,2)这个区间长度极小的区间,这相当于物理上的质点. 当我们处理的问题上与其区间长

POJ 3468 线段树(成段更新,区间求和)

题目链接:http://poj.org/problem?id=3468 题意:给定一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和,对每次询问输出结果. 这个线段树运用了应用了add域优化,每个节点除了用value记录当前节点对应区间元素的和之外,还要用add域记录当前节点对应区间每个元素的增量.这样,没必要每次更新都要更新value更新到最底层每一个点,只需要将增量记录在某父节点的add域中即可,如果下次查询或者更新操作的是该父节点对应区间的子区间,

POJ 2528 Mayor&#39;s posters (hash+线段树成段更新)

题意:有一面墙,被等分为1QW份,一份的宽度为一个单位宽度.现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW.后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报.现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到.) 思路:简单的成段更新,但是数据量是1千万,会MT,所以要区间压缩(离散化),保证覆盖的关系不变,离散化的时候有个易错的细节,poj数据水了,这个易错点引用h

POJ 2528 Mayor&#39;s posters 线段树成段更新+离散化

题目来源:POJ 2528 Mayor's posters 题意:很多张海报贴在墙上 求可以看到几张海报 看那两张图就行了 第一张俯视图 思路:最多2W个不同的数 离散化一下 然后成段更新 a[rt] = i代表这个区间是第i张报纸 更新玩之后一次query cover[i]=1代表可以看到第i张报纸 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const

POJ2528 Mayor&#39;s posters 【线段树】+【成段更新】+【离散化】

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 39795   Accepted: 11552 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

POJ训练计划2528_Mayor&#39;s posters(线段树/成段更新+离散化)

解题报告 地址传送门 题意: 一些海报,覆盖上去后还能看到几张. 思路: 第一道离散化的题. 离散化的意思就是区间压缩然后映射. 给你这么几个区间[1,300000],[3,5],[6,10],[4,9] 区间左右坐标排序完就是 1,3,4,5,6,9,10,300000; 1,2,3,4,5,6, 7 ,8; 我们可以把上面的区间映射成[1,8],[2,4],[5,7],[3,6]; 这样就节省了很多空间. 给线段染色, lz标记颜色. #include <map> #include <

POJ训练计划2528_Mayor&amp;#39;s posters(线段树/成段更新+离散化)

解题报告 id=2528">地址传送门 题意: 一些海报,覆盖上去后还能看到几张. 思路: 第一道离散化的题. 离散化的意思就是区间压缩然后映射. 给你这么几个区间[1,300000],[3,5],[6,10],[4,9] 区间左右坐标排序完就是 1,3,4,5,6,9,10,300000; 1,2,3,4,5,6, 7 ,8; 我们能够把上面的区间映射成[1,8],[2,4],[5,7],[3,6]; 这样就节省了非常多空间. 给线段染色, lz标记颜色. #include <ma

POJ 2528 Mayor&#39;s posters(线段树区间染色+离散化或倒序更新)

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 59239   Accepted: 17157 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

poj 2528 Mayor&#39;s posters(线段树 离散化 区间更新 贴海报)

     这个题目本来对大神来说可能是水题, 对我就不行了,昨晚非折腾到下半夜一点 搞定, 并且可以总结出 ,只有把问题想清楚,或着看人家解题报告自己把问题和代码思路 搞清楚,才能谈的上调bug,否则根本就不知道错在哪儿.说说这个题目的理解,他是如何转化为线段树问题的呢?我们知道线段树有一个区间更新的东西,每张海报的宽度不就是一个区间么?那么我们可以用一棵树中的部分结点 来表示整张海报的可视部分,也就是说每个结点只允许表示一张完整的或着不完整的海报(好几个结点才可以表示成完整的一张海报),那么如