[日常训练] Lost My Music

分析 斜率 + 倍增

  • 由题可知: cvcudis(u,v)=cvcudepudepv=cucvdepudepv
  • (depu,cu) 看做一个点,就相当于要求 u 与其祖先的最大斜率
  • 显然,最大斜率在u与其祖先组成的下凸壳上(斜率递增)
  • 因为要在树上做,暴力求每个点的下凸壳栈可能一次会弹掉很多点,时间复杂度难以承受
  • 考虑到斜率递增,就可以用倍增的思想,记 pre[u][k] 表示点 u 在与其祖先组成的下凸壳上往上走2k条边到达的点,则每次通过该数组找到可以相接的点 v ,直接令pre[u][0]=v,再维护出其余 pre[u][k](k>0) 即可
  • 时间复杂度显然为 O(nlogn)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>

using namespace std;

namespace INOUT
{
    const int S = 1 << 20;
    char frd[S], *hed = frd + S;
    const char *tal = hed;

    inline char nxtChar()
    {
        if (hed == tal)
            fread(frd, 1, S, stdin), hed = frd;
        return *hed++; 
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = nxtChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = nxtChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res; 
    } 
};
using namespace INOUT;

const int N = 5e5 + 5;
const double eps = 1e-10;
int c[N], dep[N], g[N][21], n; double rs[N];
struct Edge
{
    int to; Edge *nxt;
}p[N], *T = p, *lst[N]; 

inline void LinkEdge(int x, int y)
{
    (++T)->nxt = lst[x]; lst[x] = T; T->to = y;
}

inline double Slo(int x, int y)
{
    return (c[x] - c[y]) / (double)(dep[x] - dep[y]);
}

inline void Dfs(int x)
{
    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to, u = x;
        dep[y] = dep[x] + 1; 
        for (int k = 19; k >= 0; --k)
            if (rs[g[u][k]] >= Slo(g[u][k], y) - eps)
                u = g[u][k];
        if (rs[u] >= Slo(u, y) - eps) u = g[u][0];

        rs[y] = Slo(y, u);
        g[y][0] = u;
        for (int k = 0; k < 19; ++k)
            g[y][k + 1] = g[g[y][k]][k];
        Dfs(y); 
    }
}

int main()
{
    freopen("lost.in", "r", stdin);
    freopen("lost.out", "w", stdout);
    n = get(); int x;
    for (int i = 1; i <= n; ++i) c[i] = get();
    for (int i = 2; i <= n; ++i)
        x = get(), LinkEdge(x, i);

    rs[0] = rs[1] = -1e18;
    for (Edge *e = lst[1]; e; e = e->nxt)
    {
        int y = e->to;
        g[y][0] = dep[y] = 1;
        rs[y] = Slo(1, y); 
        Dfs(y);
    }

    for (int i = 2; i <= n; ++i)
        printf("%.10lf\n", -rs[i]);

    fclose(stdin); fclose(stdout);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值