1. 程式人生 > 實用技巧 >【CF932F】Escape Through Leaf

【CF932F】Escape Through Leaf

題目

題目連結: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;
}