• 【CF932F】Escape Through Leaf


    题目

    题目链接:https://codeforces.com/problemset/problem/932/F
    有一颗 (n) 个节点的树(节点从 (1)(n) 依次编号)。每个节点有两个权值,第i个节点的权值为 (a_i,b_i)

    你可以从一个节点跳到它的任意一个子节点上。从节点 (x) 跳到节点 (y) 一次的花费为 (a_x imes b_y)。跳跃多次走过一条路径的总费用为每次跳跃的费用之和。请分别计算出每个节点到达树的每个叶子节点的费用中的最小值。

    思路

    (f[x]) 表示点 (x) 跳到叶子的最小费用,显然有

    [f[x]=min_{yinmathrm{sub}(x)}(f[y]+a[x]b[y]) ]

    发现这个东西就是在其子树内的若干斜率为 (b),截距为 (a) 的直线中取 (x=a) 时最小值。无脑上 dsu on tree 和李超线段树即可。
    时间复杂度 (O(nlog^2 n))
    事实上根据洛谷题解上的证明,李超线段树合并可以做到 (O(nlog 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;
    }
    
  • 相关阅读:
    团队管理-每日站会,代码审查,结对编程
    Linux awk命令详解
    【Vegas原创】Excel中,日期和时间用&连接后格式不正确的解决方法
    SQLServer 数据库变成单个用户后无法访问问题的解决方法
    【Vegas原创】查询SQL Server更改记录的语句
    【Vegas原创】SQL Server 只安装客户端的方法
    IT? 挨踢
    64位Windows无法打开会声会影X5的解决方法
    小型IT部门建设之我见
    要熟练掌握的七个人生工具
  • 原文地址:https://www.cnblogs.com/stoorz/p/14048850.html
Copyright © 2020-2023  润新知