CF932F Escape Through Leaf

III.III.CF932F Escape Through Leaf

明显DP式很容易写出;然后观察发现其就是子树中一堆函数 \(y=kx+b\) 中对于某个 \(x\)\(y\) 的最小值,于是线段树合并李超树就OK了。

需要注意的是,李超树因为每个节点都存了一条直线(相当于标记永久化),因此在合并点 \(x\)\(y\) 时,要先合并 \(x\)\(y\) 的儿子们,然后将 \(y\) 上存的东西并到 \(x\) 子树中。可以发现复杂度仍是 \(O(n\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[100100],b[100100],rt[100100],cnt,m;
vector<int>v[100100],u;
ll res[100100];
#define mid ((l+r)>>1)
struct Line{int k;ll b;Line(){k=0,b=0x3f3f3f3f3f3f3f3f;}Line(int K,ll B){k=K,b=B;}ll f(int x){return 1ll*k*x+b;}};
struct SegTree{int lson,rson;Line l;}seg[8001000];
void modify(int &x,int l,int r,Line L){
	if(!x)x=++cnt;
	if(seg[x].l.f(u[mid])>L.f(u[mid]))swap(L,seg[x].l);
	if(seg[x].l.f(u[l])>L.f(u[l]))modify(seg[x].lson,l,mid,L);
	if(seg[x].l.f(u[r])>L.f(u[r]))modify(seg[x].rson,mid+1,r,L);
}
void merge(int &x,int y,int l,int r){
	if(!x){x=y;return;}if(!y)return;
	if(l!=r)merge(seg[x].lson,seg[y].lson,l,mid),merge(seg[x].rson,seg[y].rson,mid+1,r);
	modify(x,l,r,seg[y].l);
}
ll query(int x,int l,int r,int P){
	if(l>P||r<P||!x)return 0x3f3f3f3f3f3f3f3f;
	ll ret=seg[x].l.f(u[P]);
	if(l!=r)ret=min(ret,min(query(seg[x].lson,l,mid,P),query(seg[x].rson,mid+1,r,P)));
	return ret;
}
void dfs(int x,int fa){
	for(auto y:v[x])if(y!=fa)dfs(y,x),merge(rt[x],rt[y],0,m-1);
	if(v[x].size()==1&&x!=1)res[x]=0;
	else res[x]=query(rt[x],0,m-1,a[x]);
	modify(rt[x],0,m-1,Line(b[x],res[x]));
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),u.push_back(a[i]);
	sort(u.begin(),u.end()),u.resize(m=unique(u.begin(),u.end())-u.begin());
	for(int i=1;i<=n;i++)scanf("%d",&b[i]),a[i]=lower_bound(u.begin(),u.end(),a[i])-u.begin();
	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
	dfs(1,0);
	for(int i=1;i<=n;i++)printf("%lld ",res[i]);puts("");
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14620743.html