nyoj 9 poj 2528 posters

线段树的离散化,因为贴海报的范围是1~1e7,肯定开不了那么大的数组,但是n的范围很小只有1e4,所以可以离散化处理,什么叫做离散化?
百度百科:离散化,把无限空间中无限的个体映射到有限的空间中去,以此提高算法的时空效率。
比如一组数据:
3
10000 100000
10000 20000
20000 100000
心算就能算出来应该是3,我们可以把10000假设成1,100000假设成10,20000假设成2,这样就成了
3
1 10
1 2
2 10
其实还可以继续缩小 因为10000最小排在第一,20000第二,100000第三,所以假设10000为1,20000为2,100000为3
即:
3
1 3
1 2
2 3
因此离散化就避免了很大的空间浪费,所以虽然说数的范围是1~1e7,但是只有2e4个数,我们通过映射,可以将这些数映射成1~20000的范围,这样就只需要开2w的数组即可,而不再需要开1e7的数组,是不是很神奇啊?哈哈~
不过应该怎么映射?
可以通过map映射,不过可能会超时,我们可以将所有的数sort一下,再去重,然后每个数都对应了一个次序,按照这个次序来建树,更新,最后再求范围就可以了,
poj:

http://poj.org/problem?id=2528

poj AC代码(nyoj wa):

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
int num[20010],k,c[20010];
struct node
{
    int left,right,value,mid;
} w[80020];
struct stu
{
    int x,y;
} a[10010];
void build(int l,int r,int tr)
{
    w[tr].value=-1;
    w[tr].left=l,w[tr].right=r;
    if(l==r)  return ;
    int mid=(l+r)/2;
    w[tr].mid=mid;
    build(l,mid,tr*2);
    build(mid+1,r,tr*2+1);
}
int Search(int x)
{
    int l=1,r=k,j;
    while(l<=r)
    {
        j=(l+r)/2;
        if(num[j]==x)
            return j;
        if(x<num[j])
            r=j-1;
        else
            l=j+1;
    }
}
void update(int l,int r,int zhi,int tr)
{
    if(l==w[tr].left&&r==w[tr].right)
    {
        w[tr].value=zhi;
            return ;
    }
    if(w[tr].value!=-1)
     w[tr*2].value=w[tr*2+1].value=w[tr].value,w[tr].value=-1;
    if(r<=w[tr].mid)
        update(l,r,zhi,tr*2);
    else if(l>w[tr].mid)
        update(l,r,zhi,tr*2+1);
    else
        update(l,w[tr].mid,zhi,tr*2),update(w[tr].mid+1,r,zhi,tr*2+1);
}
void searchtree(int tr)
{
    if(w[tr].value!=-1)
        {c[w[tr].value]++;return;}
    if(w[tr].left==w[tr].right)  {return;}
    searchtree(tr*2),searchtree(tr*2+1);
}
int main()
{
    int nn;
    scanf("%d",&nn);
    while(nn--)
    {
        memset(c,0,sizeof(c));
        int n,i;
        k=0;
        scanf("%d",&n);
        for(i=1; i<=n; i++)
            scanf("%d%d",&a[i].x,&a[i].y),num[++k]=a[i].x,num[++k]=a[i].y;
        sort(num+1,num+k+1);
        k=unique(num+1,num+k+1)-num-1;
        build(1,k,1);
        for(i=1; i<=n; i++)
        {
            int x=Search(a[i].x);
            int y=Search(a[i].y);
            update(x,y,i,1);
        }
        searchtree(1);
        int s=0;
        for(i=1; i<=n; i++)
            if(c[i])
                s++;
        printf("%d\n",s);
    }
}

然后换个oj提交

http://acm.nyist.net/JudgeOnline/problemset.php

这是会惊奇的发现wa了,为什么?
再来测一组数据
3
1 10
1 4
6 10
实际结果应该为3,但是代码的结果为2,为什么?
因为映射过程中成了:
3
1 4
1 2
3 4
不难理解为什么代码结果和实际为什么不一样,但是poj后台数据水,就a了,,幸亏我nyoj后台叼~,哈哈,,
改进代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<set>   //set用来记录海报个数
#include<iostream>
using namespace std;
set<int>s;   //set容器
int num[20010],k;
struct node
{
    int left,right,value,mid;
} w[40020];
struct stu
{
    int x,y;
} a[10010];
void build(int l,int r,int tr)   //建树,但是不是一般的建树法
{
    w[tr].value=-1;
    w[tr].left=l,w[tr].right=r;
    int mid=(l+r)/2;
    w[tr].mid=mid;
    if(l+1<r)   //注意此处的建树法
    {
        build(l,mid,tr*2);
        build(mid,r,tr*2+1);
    }
}
int Search(int x)  //二分查找用来查找这个数对应的是离散化的哪个数字,
{
    int l=1,r=k,j;
    while(l<=r)
    {
        j=(l+r)/2;
        if(num[j]==x)
            return j;
        if(x<num[j])
            r=j-1;
        else
            l=j+1;
    }
}
void update(int l,int r,int zhi,int tr)  //区间更新
{
    if(l==w[tr].left&&r==w[tr].right)    //若查找到此区间,则“贴上海报”
    {
        w[tr].value=zhi;
        return ;
    }
    if(w[tr].value!=-1)  //若此区间没有找到,且此处有海报,就将其分成小块,
        w[tr*2].value=w[tr*2+1].value=w[tr].value,w[tr].value=-1;
    if(r<=w[tr].mid)
        update(l,r,zhi,tr*2);
    else if(l>=w[tr].mid)
        update(l,r,zhi,tr*2+1);
    else
        update(l,w[tr*2].right,zhi,tr*2),update(w[tr*2+1].left,r,zhi,tr*2+1);
}
void searchtree(int tr)  //查询
{
    if(w[tr].value!=-1)
      s.insert(w[tr].value);   //找到海报就放进set容器中,同时这个区间的子区间就不再统计,
    else if(w[tr].right>w[tr].left+1) 
    {
        searchtree(2*tr);
        searchtree(2*tr+1);
    }
}
int main()
{
    int nn;
    scanf("%d",&nn);
    while(nn--)
    {
        int n,i;
        k=0;
        scanf("%d",&n);
        for(i=1; i<=n; i++)
            scanf("%d%d",&a[i].x,&a[i].y),a[i].x--,num[++k]=a[i].x,num[++k]=a[i].y;  //num数组用来排序,便于离散化,至于为什么要a[i]--,自己想想,哈哈~
        sort(num+1,num+k+1);   //num排序,
        k=unique(num+1,num+k+1)-num-1;  //用了unique函数,目的是去除排序后的重复数字,
        build(1,k,1);  //建树
        for(i=1; i<=n; i++)  //n次更新
        {
            int x=Search(a[i].x);
            int y=Search(a[i].y);
            update(x,y,i,1);
        }
        searchtree(1);  //查询有多少个海报
        printf("%d\n",s.size());  //输出海报个数
        s.clear();  //清空set容器
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值