背景:
逃了物理竞赛,机房就
2
2
2人。
题目传送门:
https://www.luogu.org/problemnew/show/P3994
题意:
有一棵树,其中第
i
i
i个点到第
j
j
j个点的贡献为
d
i
s
(
i
,
j
)
∗
P
i
+
Q
i
dis(i,j)*P_i+Q_i
dis(i,j)∗Pi+Qi.若
i
i
i是
j
j
j的祖先,则保证
P
i
≤
P
j
P_i≤P_j
Pi≤Pj。
思路:
显然是一个在树上维护的斜率优化的
D
P
DP
DP。
对于一条根到叶子节点的路径的路径,它的斜率一定是单调的。那就好办了,因为它们就可以放在一起维护了。而对于不在同一棵子树内的儿子节点,回溯时注意单调队列内的点被删除的放回即可。
设
f
i
f_i
fi表示
i
i
i点到
1
1
1号根节点的路径的最小值。
则
f
i
=
∑
j
∈
i
.
a
n
c
e
s
t
o
r
f
j
+
P
i
∗
d
i
s
(
i
,
j
)
+
Q
i
f_i=\sum_{j∈i.ancestor}{f_j+P_i*dis(i,j)+Q_i}
fi=∑j∈i.ancestorfj+Pi∗dis(i,j)+Qi。
由于
i
,
j
i,j
i,j在同一条链上,因此
d
i
s
(
i
,
j
)
=
d
i
s
(
i
,
1
)
−
d
i
s
(
j
−
1
)
dis(i,j)=dis(i,1)-dis(j-1)
dis(i,j)=dis(i,1)−dis(j−1)。
设
d
i
s
i
=
d
i
s
1
,
i
dis_i=dis_{1,i}
disi=dis1,i,则:
f
i
=
f
j
+
∑
j
.
a
n
c
e
s
t
o
r
P
i
(
d
i
s
i
−
d
i
s
j
)
+
Q
i
f_i=f_j+\sum_{j.ancestor}{P_i(dis_i-dis_j)+Q_i}
fi=fj+∑j.ancestorPi(disi−disj)+Qi。
得到斜率方程:
f
j
−
f
k
d
i
s
j
−
d
i
s
k
<
p
i
\frac{f_j-f_k}{dis_j-dis_k}<p_i
disj−diskfj−fk<pi。
套进去即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
int n,m,len=0,head,tail;
struct node{int x,y,z,next;} a[2000010];
int last[1000010];
LL p[1000010],q[1000010],dis[1000010],f[1000010];
int que[1000010];
LL calc1(int x,int y)
{
return dis[y]-dis[x];
}
LL calc2(int x,int y)
{
return f[y]-f[x];
}
double calc(int x,int y)
{
return (double)(calc2(x,y))/(double)(calc1(x,y));
}
void ins(int x,int y,int z)
{
a[++len]=(node){x,y,z,last[x]}; last[x]=len;
}
int last_head[1000010],last_tail[1000010],tmp[1000010],pos[1000010];
void dfs(int x,int fa)
{
last_head[x]=head,last_tail[x]=tail;
while(head<tail&&calc(que[head],que[head+1])<=(double)p[x]) head++;
f[x]=f[que[head]]+p[x]*calc1(que[head],x)+q[x];
while(head<=tail&&calc(que[tail],x)<=calc(que[tail-1],que[tail])) tail--;
tail++;
tmp[x]=que[tail];
pos[x]=tail;
que[tail]=x;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
dis[y]=dis[x]+a[i].z;
dfs(y,x);
}
que[pos[x]]=tmp[x];
head=last_head[x],tail=last_tail[x];
}
int main()
{
int x,z;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d %d %lld %lld",&x,&z,&p[i],&q[i]);
ins(i,x,z),ins(x,i,z);
}
head=1;
tail=0;
que[0]=0;
dfs(1,0);
for(int i=2;i<=n;i++)
printf("%lld\n",f[i]);
}