HOJ 2691 Nail III解题报告

Nail III

墙上有n个钉子,编号为1, 2, …, n。其中钉子i的横坐标为i,纵坐标初始为xi。可以进行如下操作:

s t v:若在高度为v处放一块横坐标范围是[s,t]的水平木板,它将下落到什么高度?换句话说,求出钉子s, s+1, s+2, …, t的纵坐标中,不超过v的最大值。如果这些钉子的高度全部大于v,则木板将落到地上,高度为0。注意,在T操作时,水平木板只是用来测试的“临时木板”,将在测试后立即被拿走,不会影响到后续测试工作。

Input

多组case,每组case第一行包含两个整数n(1 ≤ n ≤ 100000), m(1 ≤ m ≤ 50000),即钉子的个数和操作的个数。第二行输入n个整数表示钉子的纵坐标xi(1 ≤ xi ≤ 1000000000);接下来m行询问。这些询问区间之间没有包含关系。

Output

为每个询问输出一行,包括一个整数表示询问结果。

Sample Input

5 3
1 3 5 7 9
2 4 6
3 5 7
1 2 1

Sample Output

5
7
1

此题是个关于区间询问的题目,数据范围比较大,得选取适当的数据结构。
起初我写的是个线段树,可能是自己写的太差劲了,一直超时。
后来听杨神来讲题解,说这道题用map做,具体的分析如下:
条件中的最后一句说:这些询问区间之间没有包含关系。这是个重要的条件,提示我们可以通过动态的增添维护一个map来查询。每个元素最多添加到map中一次,删除一次,总的复杂度是Nlog(N)级别的。
具体做法如下:
  1. 定义一个map,其key值代表高度,另一个值代表钉子的序号。
  2. 将区间排序,按照左端点从小到大排序
  3. 对于每个区间,讲此区间内未入map的钉子加入进去(这个过程是有序的,因为区间已经排好序)。
  4. 调用map的lower_bound函数,查询键值>=key的第一个元素,然后判断它是否在当前查询的区间内,不是的话删除掉,是的话输出
  5. 再次将区间排序,按照输入的顺序排

代码如下:

#include <iostream>
#include <algorithm>
#include <map>
#define N 100010
#define M 50010
using namespace std;
struct data
{
    int r,l,v,d,b;                                  //r、l分别为区间的左右端点,v为查询的高度,d为输入时的顺序,b为答案
    void input(int k)
    {
        d=k;
        scanf("%d %d %d",&r,&l,&v);
    }
}p[M];
int a[N];
map<int,int>g;
bool cmp1(const data a,const data b)  //按照区间左端点排序的比较函数
{
    return a.r<b.r;
}
bool cmp2(const data a,const data b) //按照输入顺序排序的比较函数
{
    return a.d<b.d;
}
int main()
{
    int n,m,i,j,k;
    while(scanf("%d %d",&n,&m)==2)
    {
        for(i=0;i<n;i++)
            scanf("%d",a+i+1);
        for(i=0;i<m;i++)
            p[i].input(i);
        sort(p,p+m,cmp1);                       //按照区间左端点排序
        g.clear();
        map<int,int>::iterator pos;
        j=1;
        k=1;
        for(i=0;i<m;i++)
        {
            for(;j<=p[i].l&&j<=n;j++)         //将未入map的钉子加入,由于upper_bound返回的是大于等于,所以改成复数加入
                g[-a[j]]=j;
            while(1)
            {
                pos=g.lower_bound(-p[i].v);  //查询
                if(pos==g.end())                 //如果没有查到,则木板掉落到地上,输出0
                {
                    p[i].b=0;
                    break;
                }
                if(pos->second<p[i].r)      //如果查到的钉子位置在此区间以前,则删除掉
                    g.erase(pos);
                else
                {
                    p[i].b=-pos->first;     //此为查到了区间内第一个小于等于v的钉子
                    break;
                }
            }
        }
        sort(p,p+m,cmp2);         //按照输入顺序排序
        for(i=0;i<m;i++)
        {
            printf("%d\n",p[i].b);
        }
    }
    return 0;
}


这道题交了好多次,一直Restricted Function,在群里问了下,还以为是超内存了,先前我是把0提前加进去的,没有判断是否是end,现在改成判断是否是end,而不是加入0,就AC了,现在想想,可能是中间某个钉子高度为0,把先前的0冲掉了,使得访问了错误的内存。还是细节问题啊。
PS:我用小号交时,没有完全去掉加入0,而只是多了个end的判断,时间0.60s,排在了第一位,当我去掉0,改用大号交,则变成了0.63s。。。囧。。。很多次了,小号比大号跑得快。。。。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值