【CF932F】Escape Through Leaf
阿新 • • 發佈:2020-11-27
題目
題目連結:https://codeforces.com/problemset/problem/932/F
有一顆 \(n\) 個節點的樹(節點從 \(1\) 到 \(n\) 依次編號)。每個節點有兩個權值,第i個節點的權值為 \(a_i,b_i\)。
你可以從一個節點跳到它的任意一個子節點上。從節點 \(x\) 跳到節點 \(y\) 一次的花費為 \(a_x\times b_y\)。跳躍多次走過一條路徑的總費用為每次跳躍的費用之和。請分別計算出每個節點到達樹的每個葉子節點的費用中的最小值。
思路
設 \(f[x]\) 表示點 \(x\) 跳到葉子的最小費用,顯然有
\[f[x]=\min_{y\in\mathrm{sub}(x)}(f[y]+a[x]b[y]) \]發現這個東西就是在其子樹內的若干斜率為 \(b\),截距為 \(a\) 的直線中取 \(x=a\) 時最小值。無腦上 dsu on tree 和李超線段樹即可。
時間複雜度 \(O(n\log^2 n)\)。
事實上根據洛谷題解上的證明,李超線段樹合併可以做到 \(O(n\log n)\) 的複雜度。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=200010,K=100001; int n,tot,rt,head[N],son[N],size[N]; ll f[N],a[N],b[N]; struct edge { int next,to; }e[N*2]; void add(int from,int to) { e[++tot].to=to; e[tot].next=head[from]; head[from]=tot; } void dfs1(int x,int fa) { size[x]=1; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) { dfs1(v,x); size[x]+=size[v]; if (size[v]>size[son[x]]) son[x]=v; } } } ll calc(int k,int x) { return b[k]*(x-K)+f[k]; } struct SegTree { int tot,ans[N*4],lc[N*4],rc[N*4]; int update(int x,int l,int r,int k) { if (!x) x=++tot; if (l==r) { if (!ans[x] || calc(k,l)<calc(ans[x],l)) ans[x]=k; return x; } int mid=(l+r)>>1; if (!ans[x] || (calc(k,l)<=calc(ans[x],l) && calc(k,r)<=calc(ans[x],r))) { ans[x]=k; return x; } if (calc(k,l)>calc(ans[x],l) && calc(k,r)>calc(ans[x],r)) return x; if (b[k]>b[ans[x]]) { if (calc(k,mid)<=calc(ans[x],mid)) rc[x]=update(rc[x],mid+1,r,ans[x]),ans[x]=k; else lc[x]=update(lc[x],l,mid,k); return x; } if (b[k]<b[ans[x]]) { if (calc(k,mid)<=calc(ans[x],mid)) lc[x]=update(lc[x],l,mid,ans[x]),ans[x]=k; else rc[x]=update(rc[x],mid+1,r,k); return x; } return x; } ll query(int x,int l,int r,int k) { if (!x) return 1e18; if (l==r) return calc(ans[x],k); int mid=(l+r)>>1; if (k<=mid) return min(calc(ans[x],k),query(lc[x],l,mid,k)); else return min(calc(ans[x],k),query(rc[x],mid+1,r,k)); } void clr(int x) { if (lc[x]) clr(lc[x]); if (rc[x]) clr(rc[x]); lc[x]=rc[x]=ans[x]=0; } }seg; void dfs3(int x,int fa) { rt=seg.update(rt,1,K*2,x); for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) dfs3(v,x); } } void dfs2(int x,int fa,bool flag) { for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa && v!=son[x]) dfs2(v,x,0); } if (son[x]) dfs2(son[x],x,1); for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa && v!=son[x]) dfs3(v,x); } if (size[x]==1) f[x]=0; else f[x]=seg.query(rt,1,K*2,a[x]+K); if (!flag) seg.clr(rt),seg.tot=rt=0; else rt=seg.update(rt,1,K*2,x); } int main() { memset(head,-1,sizeof(head)); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); for (int i=1;i<=n;i++) scanf("%lld",&b[i]); for (int i=1,x,y;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1,0); dfs2(1,0,0); for (int i=1;i<=n;i++) printf("%lld ",f[i]); return 0; }