#1992. 购票(ticket)

题目描述

题解

考虑啥限制都没有的话,也就是在一个序列上且没有 $l$ 的限制,那就是 $dp$ 然后斜率优化

$f_i$ 表示 $i$ 的答案, $f_i=min(f_j+(d_i-d_j)p_i+q_i)$ ,其中 $d_i$ 表示 $s$ 的前缀和,可以化成斜率优化的式子

由于斜率不递增所以在凸包上二分即可

考虑 $l$ 的限制的话,那我们可以做类似 $cdq$ 的东西,也就是考虑 $[l,mid]$ 对 $[mid+1,r]$ 的影响,那可以对右区间以最前能取到的点从大到小排序,然后双指针加入凸包即可

考虑在一棵树上的话,那我们考虑点分治,即对于树 $u$ 求出了重心 $rt$ ,考虑 $(u,rt)$ 路径上的点对 $rt$ 子树的影响,那我们可以先除去 $rt$ 子树内的点(除了 $rt$ ),然后递归求出剩下点的 $dp$ 值,然后我们可以像刚刚的操作即把 $rt$ 子树内除了 $rt$ 的点按照最高能取到的点按深度从大到小排序,然后双指针加入凸包即可

效率: $O(nlog^2n)$

代码

#include <bits/stdc++.h>
#define LL long long
#define db long double
using namespace std;
const int N=2e5+5;
int n,fa[N],o,rt,sz[N],son[N],a[N],hd[N],V[N],nx[N],t,S[N],tp;
LL d[N],s[N],p[N],q[N],g[N],f[N];bool vis[N];
bool cmp(int A,int B){
    return d[A]-g[A]>d[B]-g[B];
}
void add(int u,int v){
    nx[++t]=hd[u];V[hd[u]=t]=v;
}
db K(int x,int y){
    return ((db)f[y]-f[x])/((db)d[y]-d[x]);
}
void Dis(int x){
    for (int i=hd[x];i;i=nx[i])
        d[V[i]]=d[x]+s[V[i]],Dis(V[i]);
}
void Sz(int x){
    sz[x]=1;
    for (int i=hd[x],v;i;i=nx[i])
        if (!vis[v=V[i]])
            Sz(v),sz[x]+=sz[v];
}
void Rt(int x){
    son[x]=o-sz[x];
    for (int i=hd[x],v;i;i=nx[i])
        if (!vis[v=V[i]])
            Rt(v),son[x]=max(son[x],sz[v]);
    if (son[x]<=son[rt]) rt=x;
}
void find(int x){
    a[++t]=x;
    for (int i=hd[x];i;i=nx[i])
        if (!vis[V[i]]) find(V[i]);
}
void work(int x){
    Sz(x);o=sz[x];rt=0;Rt(x);
    if (o==1){vis[x]=1;return;}
    for (int i=hd[rt];i;i=nx[i]) vis[V[i]]=1;
    int u=rt;work(x);tp=t=0;
    for (int i=hd[u];i;i=nx[i])
        vis[V[i]]=0,find(V[i]);
    sort(a+1,a+t+1,cmp);int X=x;x=u;
    for (int i=1,v,l,r,j;i<=t;i++){
        v=a[i];
        while(d[u]>=d[X] && d[u]>=d[v]-g[v]){
            while(tp>1 && K(S[tp-1],u)>=K(S[tp-1],S[tp])) tp--;
            S[++tp]=u;u=fa[u];
        }
        if (!tp) continue;l=1;r=tp;
        while(l<r){
            j=(l+r+1)>>1;
            if (K(S[j],S[j-1])>=p[v]) l=j;
            else r=j-1;
        }
        f[v]=min(f[v],f[S[l]]+(d[v]-d[S[l]])*p[v]+q[v]);
    }
    vis[x]=1;
    for (int i=hd[x];i;i=nx[i]) work(V[i]);
}
int main(){
    cin>>n>>tp;son[0]=1e9;d[0]=-1;
    for (int i=2;i<=n;i++)
        scanf("%d%lld%lld%lld%lld",
        &fa[i],&s[i],&p[i],&q[i],&g[i]),
        f[i]=2e18,add(fa[i],i);
    Dis(1);work(1);
    for (int i=2;i<=n;i++)
        printf("%lld\n",f[i]);
    return 0;
}

 

posted @ 2020-02-09 21:30  xjqxjq  阅读(190)  评论(0编辑  收藏  举报