[HDU5956]The Elder

Posted cjfdf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HDU5956]The Elder相关的知识,希望对你有一定的参考价值。

题面在这里

题意

一个王国中的所有城市构成了一棵有根树,其根节点为首都,编号为1
树有边权,城市的记者每次向祖先移动\(d\)的路程需要的代价为\(d^2\)
如果祖先不是根还需要加上\(p\),求每个非根节点的记者移动到首都的最小代价

sol

考虑朴素的DP,设\(f[i]\)表示第\(i\)号节点到达根节点的最小代价,
\(s[i]\)表示\(i\)到根节点的距离,那么有
\[f[i]=min_{j\in ancestor(i)}(f[j]+(s[i]-s[j])^2+p)\]
初始状态为\(f[1]=-p\)

斜率优化:
\[f[i]=min_{j\in ancestor(i)}(f[j]+s[j]^2-2s[i]s[j])+s[i]^2+p\]
那么考虑插点\((2s[j],f[j]+s[j]^2)\),询问斜率\(k_i=s[i]\);

树上斜率优化——可持久化单调队列

由于是在树上做,那么我们需要支持撤销前一次的操作;
我们对于记录一个栈,记录推到这个节点时弹出了哪些节点,
撤回的时候在队列中删掉当前节点,把之前弹出的节点放回去即可
这样的时间复杂度应该仍为\(O(n)\)

代码

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e8;
const int N=1000010;
il ll read(){
    RG ll data=0,w=1;RG char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar();
    if(ch==‘-‘)w=-1,ch=getchar();
    while(ch<=‘9‘&&ch>=‘0‘)data=data*10+ch-48,ch=getchar();
    return data*w;
}

il void file(){
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
}

ll T,n,p,f[N],s[N];
int head[N],nxt[N<<1],to[N<<1],val[N<<1],cnt;
il void add(ll u,ll v,ll w){
    to[++cnt]=v;
    nxt[cnt]=head[u];
    val[cnt]=w;
    head[u]=cnt;
}

int L=1,R;ll qx[N],qy[N];
int cal[N],top;ll calx[N],caly[N];
il void cancel(int u){
    R--;
    while(cal[top]==u){
        R++;qx[R]=calx[top];qy[R]=caly[top];top--;
    }
}

il void insert(int u,ll x,ll y){
    while(L<R&&(qy[R]-qy[R-1])*(x-qx[R])>=(y-qy[R])*(qx[R]-qx[R-1])){
        cal[++top]=u;calx[top]=qx[R];caly[top]=qy[R];R--;
    }
    R++;qx[R]=x;qy[R]=y;
}

il int check(ll k,int mid){
    if(mid!=R&&k*(qx[mid+1]-qx[mid])>qy[mid+1]-qy[mid])
        return 1;
    if(mid!=L&&k*(qx[mid]-qx[mid-1])<qy[mid]-qy[mid-1])
        return -1;
    return 0;
}
//二分斜率
il ll query(ll k){
    RG int l=L,r=R,mid,ret;
    while(l<=r){
        mid=(l+r)>>1;
        ret=check(k,mid);
        if(!ret)return qy[mid]-k*qx[mid];
        else if(ret==1)l=mid+1;
        else if(ret==-1)r=mid-1;
    }
}

void dfs_DP(int u,int fa){
    if(u!=1){
        f[u]=query(s[u])+s[u]*s[u]+p;
        insert(u,2*s[u],f[u]+s[u]*s[u]);
    }
    else insert(u,0,-p);
    for(RG int i=head[u];i;i=nxt[i]){
        RG int v=to[i];if(v==fa)continue;
        s[v]=s[u]+val[i];dfs_DP(v,u);
    }
    cancel(u);
}

il void solve(){
    memset(head,0,sizeof(head));cnt=0;L=1;R=0;
    n=read();p=read();
    RG ll u,v,w;
    for(RG int i=1;i<n;i++){
        u=read();v=read();w=read();
        add(u,v,w);add(v,u,w);
    }

    dfs_DP(1,0);
    
    RG ll maxn=0;
    for(RG int i=1;i<=n;i++)
        maxn=max(maxn,f[i]);
    printf("%lld\n",maxn);
}

int main()
{
    T=read();while(T--)solve();return 0;
}

以上是关于[HDU5956]The Elder的主要内容,如果未能解决你的问题,请参考以下文章

[HDU5956]The Elder

hdu 5956 The Elder 2016ACM/ICPC沈阳赛区现场赛I

HDU 5956 The Elder (树上斜率DP)

HDU - 59562016ACM/ICPC亚洲区沈阳站I - The Elder 树上斜率优化dp

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

算法5956. 找出数组中的第一个回文字符串(java / c / c++ / python / go / rust)