题意

你从一个二维平面上开始做自由落体运动。重力加速度 $g=10m/s^2$,但是你可以随时调整你在水平方向上的分速度 $v_x \in [-1,1]$。你一开始在点 $(sx,sy)$,但是平面上有 $n$ 个云朵,第 $i$ 个位于 $(x_i,y_i)$,有密度 $c_i$。表示如果你到了 $(x_i,y_i)$,会立刻停止运动 $c_i$ 秒,并在 $c_i$ 秒后将竖直方向分速度重置为 $0$。要求你控制这个人,使得在落到 $y=0$ 的时间尽量大。

$n \leq 5 \times 10^4,x_i,y_i \leq 10^4$。

题解

考虑 dp。设 $f_i$ 表示到达云朵 $i$ 的最长时间。我们将所有云朵按照 $y_i$ 排序,转移考虑枚举 $j$ 可以到达 $i$,发现必须满足

$$ \frac{1}{2}g\Delta x^2 \leq \Delta y $$

所以这告诉我们有用的 $\Delta x$ 只有 $O(\sqrt y)$ 个!枚举这样的 $\Delta x$,相当于现在我们可以从 $x_j = x_i\pm \Delta x, y_j \geq y_i+5\Delta x^2$ 的点转移。

直接转移复杂度看起来还是 $O(n^2)$ 的,但是我们发现,对于 $x_j$ 相等的所有点,由于都可以到达 $i$,所以我们贪心选择最下面的点经过一定是最优的(因为这样会经过最多的云朵,也会减速最多次),所以我们只需要对于每个横坐标维护一下这些横坐标的点,转移二分到 $\geq y_i + 5 \Delta x^2$ 的位置然后从这个点转移即可。复杂度 $O(n\sqrt n \log n)$ 但是很难跑满。

代码

#include <bits/stdc++.h>

#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb emplace_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;
const int MAX = 20000 + 5;
int n;
int sx,sy;

struct Node{
    int x,y,c;
    Node(int x=0,int y=0,int c=0) : x(x),y(y),c(c) {}
}a[MAXN];

DB f[MAXN];
std::vector<P<int,int> > S[MAXN];

inline void insert(int i){
    int o = a[i].x+MAX;
    S[o].pb(-a[i].y,i);
}

inline int query(int x,int y){// at x, >= y
    x += MAX;
    int p = std::upper_bound(all(S[x]),MP(-y,(int)1e9))-S[x].begin()-1;
    if(p < 0 || p >= SZ(S[x])) return -1;
    return S[x][p].se;
}

int main(){
    scanf("%d%d%d",&n,&sx,&sy);
    FOR(i,1,n) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
    a[++n] = Node(sx,sy,0);
    std::sort(a+1,a+n+1,[&](const Node &a,const Node &b){return a.y > b.y;});
    f[1] = 0;insert(1);
    FOR(i,2,n){
        int x = a[i].x,y = a[i].y;
        f[i] = -1e100;
        FOR(dx,0,sy){
            if(y+5ll*dx*dx > sy) break;
            int t = query(x-dx,y+5ll*dx*dx);
            if(t != -1){
                f[i] = std::max(f[i],f[t]+std::sqrt((a[t].y-a[i].y)/5.0));
            }
            t = query(x+dx,y+5ll*dx*dx);
            if(t != -1){
                f[i] = std::max(f[i],f[t]+std::sqrt((a[t].y-a[i].y)/5.0));
            }
        }
        if(f[i] != -1e100){
            f[i] += a[i].c;
            insert(i);
        }
    }
    DB ans = -1e100;
    FOR(i,1,n) ans = std::max(ans,f[i]+std::sqrt(2.0*a[i].y/10));
    printf("%.10f\n",ans);
    return 0;
}
Last modification:May 15th, 2021 at 11:55 am
如果觉得我的文章对你有用,请随意赞赏