ZOJ 3888 Twelves Monkeys (优先队列+预处理)

ZOJ 3888 Twelves Monkeys (优先队列+预处理)

tags: acm


(题目链接)

其实是挺简单的题,就是要找次小值.但是我开始的时候没想到用优先队列,强行暴力维护TLE了两遍,优化矬了WA一次,无奈改用优先队列后又错了好几次,后来花了好长时间(真的很久)才发现忘记重载小于号了(那时候我就觉得自己是一个煞笔orz)最后终于AC.

题意:

有M个时间机器,每个时间机器能让你从x年回到y年( 1yxn50000 ).现在你有一次使用时间机器的机会,问有哪些年你有两种方法到达.每组输入有多个询问,每个询问输入当前年份.

解析:

假如当前年份为p,则可以使用所有x值大于等于p的时间机器.而在这些机器中,我们只需要关注y值最小的两个就好了.y的最小值到当前年份之间的年份可以访问一次,而y次小值到当前年份之间能被访问两次.因此整个问题就转化为找到x>=p的时间机器中y第二小的那个.需要注意的是存在次小值大于p的情况.由于询问较多,我们可以先进行一次预处理,得到[1,n]范围内的所有p值对应的答案,将其存在ans[p]中,询问时输出对应的答案即可.

据说还可以用线段树(主席树)来做,不过我不是很明白,也没再多花时间了.

代码:

运行时间:550ms

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>

#define MAXN    50000+100

using namespace std;

struct E
{
    int from, to;
}arc[MAXN];

bool operator < (E a, E b)
{
    if (a.from != b.from)
        return a.from > b.from;
    return a.to < b.to;
}

struct node
{
    int x;
    node(int xx=0) { x = xx; }
}nnode;

bool operator < (node a, node b)
{
    return a.x>b.x;
}

int ans[MAXN];

int main()
{
    int N, M, q;
    while (scanf("%d%d%d", &N, &M, &q) != EOF)
    {
        for (int i = 0; i < M; i++)
        {
            scanf("%d%d", &arc[i].from, &arc[i].to);
        }
        sort(arc, arc + M);
        priority_queue<node> Q;
        memset(ans, 0, sizeof(ans));
        int pos = 0;
        for (int i = N; i >= 0; i--)
        {
            while (pos < M && arc[pos].from >= i)
            {
                nnode.x = arc[pos++].to;
                Q.push(nnode);
            }
            if (Q.size() >= 2)
            {
                int m1 = Q.top().x;
                Q.pop();
                int m2 = Q.top().x;
                Q.pop();
                if (m2 <= i)
                {
                    ans[i] = i - m2;
                }
                else
                    ans[i] = 0;
                Q.push(m1);
                Q.push(m2);
            }
            else
                ans[i] = 0;
        }
        int k;
        for (int i = 0; i < q; i++)
        {
            scanf("%d", &k);
            printf("%d\n", ans[k]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值