题意:给你一颗树,每条边有边权,定义每条路径长度为路径上相邻边 权值差的平方和。
树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?反正本弱鸡不会