【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;
}
上一篇:vxlan二层互通-有隧道方式


下一篇:数据分析模型之决策树及随机森林