1. 程式人生 > >5956 The Elder (斜率優化dp)

5956 The Elder (斜率優化dp)

題目連結

題目大意:有一棵樹形的青蛙王國,青蛙長者在根結點,其他的每個結點都有跑得很快的青蛙記者負責向根節點傳遞新聞,每隻青蛙跑長度為L的距離需要花費L^2的時間,途中可以在其他結點把新聞傳給另一隻青蛙,但需要額外花費P點時間。求從每個結點將新聞傳遞給根節點所需要花費的最短時間中的最長時間。

解法:根據題意有狀態轉移方程:dp[i]=min(dp[j]+(sum[i]-sum[j])^{2}+P)(0<=j<i),根據這個方程跑一遍dfs即可,注意回溯的時候撤銷操作。

直接轉移的複雜度是O(n^2),但這個方程滿足斜率單調性,可以用斜率優化降低到O(n)的複雜度。

關於斜率優化,可以參考某位dalao的部落格:傳送門

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=1e5+100;
int n;
ll a[N],sum[N],d[N],k;
int q[N],hd,tl;
int head[N],to[N<<1],nxt[N<<1],nEdge;
ll cost[N<<1],ans;

void AddEdge(int u,int v,ll c)
{
    nxt[nEdge]=head[u],to[nEdge]=v,cost[nEdge]=c,head[u]=nEdge++;
}

ll dy(int a,int b)
{
    return d[a]+sum[a]*sum[a]-d[b]-sum[b]*sum[b];
}

ll dx(int a,int b)
{
    return 2*(sum[a]-sum[b]);
}

void dfs(int u,int i,int fa)
{
    int oldhd=hd,oldtl=tl;
    while(hd<tl&&dy(q[hd+1],q[hd])<=dx(q[hd+1],q[hd])*sum[i])hd++;
    d[i]=d[q[hd]]+(sum[i]-sum[q[hd]])*(sum[i]-sum[q[hd]])+k;
    ans=max(ans,d[i]);
    while(hd<tl&&dy(i,q[tl])*dx(q[tl],q[tl-1])<=dx(i,q[tl])*dy(q[tl],q[tl-1]))tl--;
    int x=q[++tl];
    q[tl]=i;
    for(int e=head[u];~e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa)continue;
        sum[i+1]=sum[i]+cost[e];
        dfs(v,i+1,u);
    }
    q[tl]=x;
    hd=oldhd,tl=oldtl;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(head,-1,sizeof head);
        nEdge=0;
        scanf("%d%lld",&n,&k);
        for(int i=1; i<n; ++i)
        {
            int u,v;
            ll c;
            scanf("%d%d%lld",&u,&v,&c);
            AddEdge(u,v,c);
            AddEdge(v,u,c);
        }
        hd=0,tl=-1;
        q[++tl]=0;
        d[0]=0;
        ans=0;
        dfs(1,1,0);
        printf("%lld\n",ans-k);
    }
    return 0;
}