MXOJ Next NOIP Mock Contest #2 B / Problem #94 Solution

闲话

当我往代码的某个位置加上了一个 = 号后,我的代码的内存占用缩小至了原来的 \(\frac{1}{2^n}\)。我并没有开玩笑。同时,运行效率也提高了不知道多少倍,因为没加等号的程序运行第 \(3\) 个样例时需要占用 10G+ 的内存,而我的电脑是撑不住的,所以我把它 Ctrl-C 了,不知道实际运行时间。而加了等号之后,所有样例运行时间都未超过 \(200\) 毫秒。

题解

考虑如下算法:

假设目前走到了位置 \(x\)。从位置 \(0\) 到位置 \(x\) 有若干种可能的方案,其中每种方案可以使用二元组 \((t,v)\) 表示。其中,\(t\) 代表走到这个位置的总用时,而 \(v\) 代表目前行进 \(1\) 个单位长度所用的时间。

我们称一个二元组集合 \(S\)精悍的,当且仅当该集合内任意两个相异二元组 \((t,v)\)\((t',v')\) 不满足 \(t\le t'\land v\le v'\)

定义一次清理为将一个二元组集合 \(S\) 转换为它的一个精悍的子集 \(S'\),并且在其中 \(t\) 的最小值尽可能小的前提下 \(|S'|\) 尽可能大。

定义一次距离为 \(x\) 的步进为将一个二元组集合 \(S\) 内的每个元素 \((t,v)\) 转换为 \((t+xv,v)\)

定义一次参数为 \((t',x')\) 的可能加油为设集合 \(S'\) 为集合 \(S\)一对一映射 \((t,v)\to (t+t',\frac{v}{x'})\),令 \(S\leftarrow S\cup S'\)

可能加油询问统称为事件

初始时,令 \(S\)\(\{(0,1),(10^9,0)\}\)。第一个元素为初始状态,第二个元素为不加油的边界条件。

接下来,将询问与加油站放在一起排序,令新的位置数组为 \(p'\),大小为 \((n+m)\)。令 \(p'_0=0\),则依次对于第 \(i\) 个事件,执行一次距离为 \((p'_i-p'_{i-1})\) 的步进,并进行清理。接下来,若该事件为询问,则答案为 \(S\) 中所有元素中最小的 \(t\) 值。否则执行一次参数为 \((t_i,x_i)\) 的可能加油,并进行清理

我无法分析上述做法的准确时间复杂度,但上述方法确实可以通过此题。

代码

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <list>
using namespace std;
const int N = 2e5 + 10, inf = 1e9;
using ld = long double;
const ld eps = 1e-7;
template <typename _Tp> inline void read(_Tp &x)
{
    char ch;
    while (ch = getchar(), !isdigit(ch))
        ;
    x = (ch ^ 48);
    while (ch = getchar(), isdigit(ch))
        x = (x << 3) + (x << 1) + (ch ^ 48);
}
template <typename _Tp, typename... _Args> inline void read(_Tp &x, _Args &...args)
{
    read(x);
    read(args...);
}
struct st
{
    ld ctm, spd;
    bool operator<(const st &x) const
    {
        if (ctm != x.ctm)
            return ctm > x.ctm;
        return spd < x.spd;
    }
};
struct evrt
{
    int p, t, x;
    bool operator<(const evrt &tx) const
    {
        if (p != tx.p)
            return p < tx.p;
        if (t != tx.t)
            return t < tx.t;
        return x < tx.x;
    }
} evnt[N];
list<st> curwk, buf;
int n, m, cur;
ld res[N];
void clr()
{
    curwk.sort();
    for (auto &i : curwk)
    {
        while (buf.size() and buf.back().spd >= i.spd)
            buf.pop_back();
        buf.emplace_back(i);
    }
    curwk.swap(buf);
    buf.clear();
}
void frwd(int x)
{
    cur += x;
    for (auto &i : curwk)
    {
        i.ctm += i.spd * x;
    }
    clr();
}
void charge(evrt a)
{
    // if (a.x == 1)
    //     return;
    int tmp = curwk.size();
    auto it = curwk.begin();
    for (int i = 0; i < tmp; i++)
    {
        curwk.emplace_back(st{it->ctm + a.t, it->spd / a.x});
        if (curwk.back().spd * (inf - cur) < eps)
            curwk.back().spd = 0;
        it++;
    }
    clr();
}
int main()
{
    freopen("ship.in", "r", stdin);
    freopen("ship.out", "w", stdout);
    read(n, m);
    curwk.emplace_back(st{0, 1});
    curwk.emplace_back(st{1e9, 0});
    for (int i = 1; i <= n; i++)
    {
        read(evnt[i].p, evnt[i].t, evnt[i].x);
    }
    for (int i = 1; i <= m; i++)
    {
        read(evnt[i + n].p);
        evnt[i + n].t = -1;
        evnt[i + n].x = i;
    }
    sort(evnt + 1, evnt + n + m + 1);
    for (int i = 1; i <= n + m; i++)
    {
        if (cur != evnt[i].p)
            frwd(evnt[i].p - cur);
        if (~evnt[i].t)
        {
            charge(evnt[i]);
            continue;
        }
        res[evnt[i].x] = curwk.back().ctm;
    }
    for (int i = 1; i <= m; i++)
    {
        printf("%.7Lf\n", res[i]);
    }
}
posted @ 2024-11-17 13:59  丝羽绫华  阅读(8)  评论(0)    收藏  举报