[NOI2014]购票
[NOI2014]购票
题解
dp式子挺简单的。。直接列就完事了。。。
\(f(i)\) \(=\) \(q(i) + p(i) \cdot d(i) + min \{ f(j)-d(j) \cdot p(i) \}\)
如果是在数列上的话,这是一个比较容易的斜率优化dp。。
但是要注意这里的 \(p(i)\) 不是单调的,所以队列前端不能删除元素,只能每次询问都二分。。
这就导致在数列上也只能做到\(O(nlogn)\)
然后考虑树上的情况,一个节点可以有多个儿子,直接模拟队列是不可行的。。
我刚开始想的是按套路进行树剖,建成线段树,然后每次都能取出一个完整的队列询问。。
这样每次询问最多问到\(O(logn)\)条链,每条链对应的线段树上的点最多为\(O(logn)\),每个点取出队列来二分也是\(O(logn)\)..
总效率就是\(O(n{log}^{3}n)\)
看洛谷上有人说这样就能过了。。但是既然有两个log的算法那还是学一下两个log的吧。。
在树上进行点分治,每次找到重心后先把与根相连的部分先递归,然后再考虑与根相连的部分对重心的各个子树的贡献。。
总效率是\(O(n{log}^{2}n)\)
\(Code\)
#include <bits/stdc++.h>
#define LL long long
#define DB double
using namespace std;
const int N=2e5+10;
const LL INF=1e18;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,op,cnt=0;
int fa[N],hed[N];
LL p[N],q[N],l[N],d[N],f[N];
bool vis[N];
struct edge{
int r,nxt;
LL w;
}e[N];
void insert(int u,int v,LL w){
e[++cnt].r=v;e[cnt].nxt=hed[u];hed[u]=cnt;e[cnt].w=w;
}
void getd(int x){
for(int i=hed[x];i;i=e[i].nxt){
d[e[i].r]=d[x]+e[i].w;
getd(e[i].r);
}
}
int mx,rt;
int sz[N];
void getrt(int x,int S){
sz[x]=1;int son=0;
for(int i=hed[x];i;i=e[i].nxt)
if(!vis[e[i].r]){
getrt(e[i].r,S);
sz[x]+=sz[e[i].r];
son=max(son,sz[e[i].r]);
}
son=max(son,S-sz[x]);
if(son<=mx){
rt=x;
mx=son;
}
}
int tp;
int st[N];
void getst(int x){
st[++tp]=x;
for(int i=hed[x];i;i=e[i].nxt) if(!vis[e[i].r]) getst(e[i].r);
}
bool cmp(int x,int y){
return d[x]-l[x]>d[y]-l[y];
}
DB slope(int k,int j){
return DB(f[k]-f[j])/(DB)(d[k]-d[j]);
}
int it;
int P[N];
void ins(int x){
while(it>=2&&slope(P[it-1],P[it])<=slope(P[it],x)) --it;
P[++it]=x;
}
int ask(LL ppp){
int l=1,r=it,mid;
while(l!=r){
mid=l+r>>1;
if(slope(P[mid],P[mid+1])>=ppp) l=mid+1;
else r=mid;
}
return P[l];
}
void sol(int x,int S){
if(S==1) return;
mx=1e9;getrt(x,S);
int y=rt;
for(int i=hed[y];i;i=e[i].nxt) {
vis[e[i].r]=1;S-=sz[e[i].r];
}
sol(x,S);
tp=0;for(int i=hed[y];i;i=e[i].nxt) getst(e[i].r);
sort(st+1,st+1+tp,cmp);
it=0;int z=y;
for(int i=1;i<=tp;++i){
int v=st[i];
while(z!=fa[x]&&d[z]>=d[v]-l[v]) {
ins(z);z=fa[z];
}
if(it){
int hhh=ask(p[v]);
f[v]=min(f[v],f[hhh]+(d[v]-d[hhh])*p[v]+q[v]);
}
}
for(int i=hed[y];i;i=e[i].nxt) sol(e[i].r,sz[e[i].r]);
}
int main(){
LL w;
scanf("%d%d",&n,&op);
for(int i=2;i<=n;++i){
scanf("%d%lld%lld%lld%lld",&fa[i],&w,&p[i],&q[i],&l[i]);
insert(fa[i],i,w);f[i]=INF;
}
getd(1);sol(1,n);
for(int i=2;i<=n;++i) printf("%lld\n",f[i]);
return 0;
}