P2305 [NOI2014] 购票 - 斜率优化
题意
给定边带权的有根树,根是 \(1\)。对于每个点 \(u>1\),给定参数 \(p_u,q_u,l_u\),求 \(\{f_n\}\) 满足:
\[f_1=0\\ f_u=\min_{v\in \mathrm{anc}_u \land \operatorname{dist}(u,v)\le l_u} \{ f_v+\operatorname{dist}(u,v)\cdot p_u\}+q_u \]\(1\le n\le 2\times 10^5,\operatorname{dist}(1,u)\le 2\times 10^{11}\)。保证边权 \(>0\),答案 \(<2^{63}\)。
题解
这里讲的是出栈序的做法。点分治以及可撤销栈的做法以后再补吧。
设 \(d_u=\operatorname{dist}(1,u)\)。我们将转移方程化为:
\[f_u=\min_{v\in \mathrm{anc}_u \land d_v\ge d_u-l_u} \{ f_v-d_vp_u\}+q_u+d_up_u \]我们 dfs 整棵树,并记录每个点 \(u\) 的出栈时间 \(o_u\)。可以发现,假如再按照 dfs 的顺序计算每个点的 \(f\),那么对于一个点 \(u\) 和它的一个祖先 \(v\),\([o_u,o_v]\) 中计算过的有且仅有 \(v\to u\) 的链上的点。
当我们要计算 \(f_u\) 时,因为边权 \(>0\),所以我们可以找到深度最浅的一个祖先 \(v\) 满足 \(d_v\ge d_u-l_u\)。原问题是 \(v\to u\) 的链上的 \(\min\),我们可以转化成 \([o_u,o_v]\) 这个区间中的 \(\min\)。
再考虑区间怎么做。这就相当于要支持:
- 在序列上某个位置放上一个点;
- 询问一个区间内的点形成的下凸壳。
因为边权 \(>0\),所以每次都相当于在凸壳中 push_back(而不是 insert),因此可以用线段树套单调栈维护,将区间查询拆成 \(\log\) 个线段树上的点即可。注意在凸壳上二分的写法。
复杂度 \(\mathcal{O}(n\log^2 n)\)。
代码
#include
#include
#include
#include
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
typedef __int128 i128;
const int N=2e5+5;
const ll Inf=0x3f3f3f3f3f3f3f3f;
ll f[N],d[N];
bool Pop(int x,int y,int z){
return i128(d[x]-d[y])*i128(f[y]-f[z])1&&Pop(stk[top-1],stk[top],x)) --top;
stk[++top]=x;
}
int QMin(ll k){
if(!top) return 0;
if(top==1) return stk[top];
int l=1,r=top;
while(l>1;
if(W(stk[mid],k)>W(stk[mid+1],k)) l=mid+1;
else r=mid;
}
return stk[l];
}
};
struct SegmentTree{
struct Node{
int l,r;Stack stk;
}t[N<<2];
void Build(int p,int l,int r){
t[p].l=l,t[p].r=r,t[p].stk=Stack(r-l+1);
if(l==r) return;
int mid=(l+r)/2;
Build(p*2,l,mid),Build(p*2+1,mid+1,r);
}
void Insert(int p,int pos,int k){
t[p].stk.Push(k);
if(t[p].l==t[p].r) return;
int mid=(t[p].l+t[p].r)>>1;
if(pos<=mid) Insert(p*2,pos,k);
else Insert(p*2+1,pos,k);
}
ll QMin(int p,int l,int r,ll k){
if(l>t[p].r||r e[N];
void Dfs1(int u){
d[u]=d[fa[u]]+s[u];
for(int v:e[u]){
Dfs1(v);
}
dfn[u]=++dfx;
}
vector psd;
vector anc;
void Dfs2(int u){
if(u!=1){
auto it=lower_bound(psd.begin(),psd.end(),d[u]-l[u]);
int v=*(anc.begin()+(it-psd.begin()));
f[u]=seg.QMin(1,dfn[u],dfn[v],p[u])+q[u]+d[u]*p[u];
}
seg.Insert(1,dfn[u],u);
psd.push_back(d[u]),anc.push_back(u);
for(int v:e[u]){
Dfs2(v);
}
psd.pop_back(),anc.pop_back();
}
int main(){
#ifndef zyz
ios::sync_with_stdio(false),cin.tie(nullptr);
#endif
cin>>n>>t;
For(i,2,n){
cin>>fa[i]>>s[i]>>p[i]>>q[i]>>l[i];
e[fa[i]].push_back(i);
}
seg.Build(1,1,n);
Dfs1(1);
Dfs2(1);
For(i,2,n) cout<