牛客第一場多校H Longest Path &&斜率dp

題意:給你一顆樹,每條邊有邊權,定義每條路徑長度爲路徑上相鄰邊 權值差的平方和。

樹dp加斜率優化

#include <bits/stdc++.h>
using namespace std;
#define N 200005
#define ll long long
#define go(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
struct no{
    int to,n,w;
};no eg[N*2];
int h[N],tot=1,n;
void add(int u,int to,int w){
    eg[++tot]={to,h[u],w};h[u]=tot++;
    eg[++tot]={u,h[to],w};h[to]=tot++;
}
ll down[N],dis[N],up[N],dp[N];
void dfsdown(int u,int fa){
    for(int i=h[u];i;i=eg[i].n){
        int to=eg[i].to;
        if(to==fa)continue;
        dis[to]=eg[i].w;
        dfsdown(to,u);
        dp[u]=max(dp[u],down[to]);
        down[u]=max(down[u],down[to]+(dis[u]-dis[to])*(dis[u]-dis[to]));
    }
}
ll x(int u){return dis[u]; }
ll y(int u){return down[u]+dis[u]*dis[u]; }
ll dx(int a,int b){return x(a)-x(b); }
ll dy(int a,int b){return y(a)-y(b); }
bool cmp(int a,int b){return dis[a]<dis[b]; }
//up[to]+c[to]^2 = 2*c[u] *c[to]  + up[u]-c[u]^2;
int qu[N],q[N];
void cal(int u,int fa){
    int n=0;
    for(int i=h[u];i;i=eg[i].n){
        int to=eg[i].to;
        if(to==fa)continue;
        q[++n]=to;
        if(fa)up[to]=up[u]+(dis[u]-dis[to])*(dis[u]-dis[to]);
    }
    sort(q+1,q+n+1,cmp);
    int l=0,r=0;
    go(i,1,n){
        int u=q[i];
        while(l<r-1&&dy(qu[r],qu[r-1])<2*dis[u]*dx(qu[r],qu[r-1]))r--;
        if(l<r)up[u]=max(up[u],down[qu[r]]+(dis[u]-dis[qu[r]])*(dis[u]-dis[qu[r]]));
        while(l<r-1&&dy(qu[r],qu[r-1])*dx(u,qu[r])<dy(u,qu[r])*dx(qu[r],qu[r-1]))r--;
        qu[++r]=u;
    }
    l=0,r=0;
    dep(i,n,1){
        int u=q[i];
        while(l<r-1&&dy(qu[r],qu[r-1])<2*dis[u]*dx(qu[r],qu[r-1]))r--;
        if(l<r)up[u]=max(up[u],down[qu[r]]+(dis[u]-dis[qu[r]])*(dis[u]-dis[qu[r]]));
        while(l<r-1&&dy(qu[r],qu[r-1])*dx(u,qu[r])>dy(u,qu[r])*dx(qu[r],qu[r-1]))r--;
        qu[++r]=u;
    }
    dp[u]=max(dp[u],up[u]);
}
  
void dfsup(int u,int fa){
    cal(u,fa);
    for(int i=h[u];i;i=eg[i].n){
        int to=eg[i].to;
        if(to==fa)continue;
        dfsup(to,u);
    }
}
  
void init(){ go(i,1,n)dp[i]=up[i]=down[i]=h[i]=0;tot=1; }
int u,to,w;
int main()
{
    while(cin>>n){
        init();
        go(i,2,n)scanf("%d%d%d",&u,&to,&w),add(u,to,w);
        dfsdown(1,0);
        dfsup(1,0);
        go(i,1,n)printf("%lld\n",dp[i]);
    }
    return 0;
}

對每個結點的子節點排序之後,正反兩邊斜率dp。

網上對這裏的講解都不是很詳細

第一遍斜率dp的時候,維護的是上凸殼,

第二遍斜率dp的時候,維護的是下凸殼。

一開始沒弄懂爲什麼要這樣維護,後來發現是和x的遞增遞減,k的遞增遞減,取最大最小值有關的。

當x,k都單調時,

當x遞增,k遞增時,

取最大值,用棧維護上凸殼, 取最小值,用隊列維護下凸殼

當x遞增,k遞減時,

取最大值,用隊列維護上凸殼,取最小值,用棧維護下凸殼

當x遞減,可以當做斜率的方向全部改變了,然後方法和遞增是相反的。

當k不單調時,就需要二分在凸殼上找答案了。

當x也不單調2時,cdq?反正本弱雞不會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章