Escape Through Leaf
Escape Through Leaf
https://ac.nowcoder.com/acm/problem/112611
分析
我们可以先写出暴力转移方程 表示
节点到子树中某个叶子节点的最小代价。那么转移方程为
可以看出这个是类似
的直线方程的,但是斜率并不单调,所以考虑李超线段树,但是从一个子树转移到另一个子树不能暴力转移,否则时间复杂度为
。所以我们考虑线段树合并,处理完子树后进行线段树合并。这样时间复杂度为
,有大佬通过势能分析,时间复杂度为
。最后跑的还是挺快。
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL N = 2e6 + 100,M = 1e5,inf = 0x3f3f3f3f3f3f3f3f;
LL a[N],b[N],n,f[N];
LL t[N],lc[N],rc[N],size,rt[N];
LL read() {LL x;scanf("%lld",&x);return x;}
LL g(LL x,LL p) {return b[x] * p + f[x];}
vector<LL> G[N];
void insert(LL &u,LL l,LL r,LL id) {
if(!u) {u = ++size;t[u] = id;return;}
LL mid = l + r >> 1;
LL val_l = g(t[u],l),val_r = g(t[u],r),val_L = g(id,l),val_R = g(id,r);
if(!t[u] || val_R <= val_r && val_L <= val_l) {t[u] = id;return;}
if(val_l <= val_L && val_r <= val_R) {return;}
insert(lc[u],l,mid,id);insert(rc[u],mid+1,r,id);
}
LL ask(LL u,LL l,LL r,LL p) {
if(!u) return inf;
if(l == r) {return g(t[u],p);}
LL ans = g(t[u],p);LL mid = l + r >> 1;
if(p <= mid) return min(ans,ask(lc[u],l,mid,p));
else return min(ans,ask(rc[u],mid+1,r,p));
}
LL merge(LL x,LL y,LL l,LL r) {
if(!x || !y) return x|y;
int mid = l + r >> 1;
insert(x,l,r,t[y]);
lc[x] = merge(lc[x],lc[y],l,mid);rc[x] = merge(rc[x],rc[y],mid+1,r);
return x;
}
void solve(LL x,LL fa) {
int son = 0;
for(auto y:G[x]) {
if(y == fa) continue;
solve(y,x);
rt[x] = merge(rt[x],rt[y],-M,M);
}
f[x] = ask(rt[x],-M,M,a[x]);
if(f[x] == inf) f[x] = 0;
insert(rt[x],-M,M,x);
}
int main() {
n = read();f[0] = inf;
for(LL i = 1;i <= n;i++) a[i] = read();
for(LL i = 1;i <= n;i++) b[i] = read();
for(LL i = 1;i < n;i++) {
LL a = read(),b = read();
G[a].push_back(b);G[b].push_back(a);
}
solve(1,0);
for(LL i = 1;i <= n;i++) printf("%lld ",f[i]);
printf("\n");
}
美的集团公司福利 724人发布
