码迷,mamicode.com
首页 > 其他好文 > 详细

CF932F 【Escape Through Leaf】

时间:2021-02-17 14:47:56      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:mat   printf   最小花费   一条直线   through   平面   ++i   min   span   

\(\text{Solution}\)

我们设 \(dp_i\) 表示编号为 \(i\) 的节点到达该树叶子节点的最小花费,那么显然我们有以下的转移方程:

\[dp_i=\begin{cases}0&i \text{ is leaf}\\\min\limits_{j \in child_s}\left\{dp_j+a_i \times b_j\right\}&\text{Otherwide}\\\end{cases} \]

直接转移是 \(\text{O}\left(n^2\right)\) 的,考虑如何优化。

我们令 \(dp_j+a_i \times b_j\)\(y\)\(a_i\)\(x\) ,则我们可以在二维平面上用 \(y=b_j \times x + dp_j\) 这条直线表示出一种转移的情况,那么我们求 \(\min\left\{dp_j+a_i \times b_j\right\}\) 等价于在若干条直线找到一条直线,使其与 \(x=a_i\) 交点的纵坐标最小,因此可以直接使用李超树维护。

可以先将树的 dfs 序处理出来,那么 dp 的转移便是一个区间,因此线段树套李超树维护一下就行了。

\(\text{Code}\)

const int N=4e5+5;
int n,a[N],b[N],dfn[N],low[N],ans[N],cnt;

vector<int> val,lcc,rcc;

struct Edge
{
	int v,ne;
}e[N*2];
int head[N],tot;

struct Line
{
	int k,b;
}c[N];

inline int f(int id,int v);
inline void add(int u,int v);
void dfs1(int u,int fa);
void dfs2(int u,int fa);
void build(int x,int l,int r);
void update1(int x,int l,int r,int p,int v);
int query1(int x,int l,int r,int L,int R,int v);
void update2(int x,int l,int r,int v);
int query2(int x,int l,int r,int v);

signed main()
{
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=n;++i) b[i]=read();
	for(int i=1;i<=n-1;++i)
	{
		int u=read(),v=read();
		add(u,v);add(v,u);
	}
	c[0].b=1e18;
	dfs1(1,0);
	cnt=0;build(1,1,n);
	for(int i=0;i<=cnt;++i)
	{
		val.push_back(0);
		lcc.push_back(0);
		rcc.push_back(0);
	}
	dfs2(1,0);
	for(int i=1;i<=n;++i)
		printf("%lld ",ans[i]);
	return 0;
}

inline int f(int id,int v)
{
	return c[id].k*v+c[id].b;
}

inline void add(int u,int v)
{
	e[++tot]=(Edge){v,head[u]};
	head[u]=tot;
}

void dfs1(int u,int fa)
{
	dfn[u]=++cnt;
	for(int i=head[u];i;i=e[i].ne)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs1(v,u);
	}
	low[u]=cnt;
}

void dfs2(int u,int fa)
{
	int fl=1;
	for(int i=head[u];i;i=e[i].ne)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs2(v,u);fl=0;
	}
	if(fl) ans[u]=0;
	else ans[u]=query1(1,1,n,dfn[u]+1,low[u],a[u]+100001);
	c[u]=(Line){b[u],ans[u]};
	update1(1,1,n,dfn[u],u);
}

void build(int x,int l,int r)
{
	cnt=max(cnt,x);
	if(l==r) return;
	build(lc,l,mid);
	build(rc,mid+1,r);
}

void update1(int x,int l,int r,int p,int v)
{
	update2(x,1,200001,v);
	if(l==r) return;
	if(p<=mid) update1(lc,l,mid,p,v);
	else update1(rc,mid+1,r,p,v);
}

int query1(int x,int l,int r,int L,int R,int v)
{
	if(L<=l&&r<=R) return query2(x,1,200001,v);
	int res=1e18;
	if(L<=mid) res=min(res,query1(lc,l,mid,L,R,v));
	if(mid+1<=R) res=min(res,query1(rc,mid+1,r,L,R,v));
	return res;
}

void update2(int x,int l,int r,int v)
{
	if(l==r)
	{
		if(f(v,l-100001)<f(val[x],l-100001))
			val[x]=v;
		return;
	}
	if(c[v].k>c[val[x]].k)
	{
		if(f(v,mid-100001)<f(val[x],mid-100001))
		{
			if(!rcc[x])
			{
				rcc[x]=++cnt;
				val.push_back(0);
				lcc.push_back(0);
				rcc.push_back(0);
			}
			update2(rcc[x],mid+1,r,val[x]),val[x]=v;
		}
		else
		{
			if(!lcc[x])
			{
				lcc[x]=++cnt;
				val.push_back(0);
				lcc.push_back(0);
				rcc.push_back(0);
			}
			update2(lcc[x],l,mid,v);
		}
	}
	if(c[v].k<c[val[x]].k)
	{
		if(f(v,mid-100001)<f(val[x],mid-100001))
		{
			if(!lcc[x])
			{
				lcc[x]=++cnt;
				val.push_back(0);
				lcc.push_back(0);
				rcc.push_back(0);
			}
			update2(lcc[x],l,mid,val[x]);val[x]=v;
		}
		else
		{
			if(!rcc[x])
			{
				rcc[x]=++cnt;
				val.push_back(0);
				lcc.push_back(0);
				rcc.push_back(0);
			}
			update2(rcc[x],mid+1,r,v);
		}
	}
	if(c[v].k==c[val[x]].k)
	{
		if(f(v,mid-100001)<f(val[x],mid-100001))
			val[x]=v;
		return;
	}
}

int query2(int x,int l,int r,int v)
{
	if(x==0) return 1e18;
	int res=f(val[x],v-100001);
	if(l==r) return res;
	if(v<=mid) return min(res,query2(lcc[x],l,mid,v));
	else return min(res,query2(rcc[x],mid+1,r,v));
}

CF932F 【Escape Through Leaf】

标签:mat   printf   最小花费   一条直线   through   平面   ++i   min   span   

原文地址:https://www.cnblogs.com/blackbird137/p/14402433.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!