zoukankan      html  css  js  c++  java
  • P1131 【ZJOI2007】 时态同步

    #$Description$ [题面](https://www.luogu.org/problem/P1131) 给你一颗有根树,你只能增加一条边的边权,最后需要使得根到每个叶子节点的距离相等 #$Solution$ 一道有点贪心意味的$DP$题,假设一开始根节点到叶子节点的最远距离为$d$,考虑到只能加边权不能减边权,**显然最终根节点到所有叶子节点的距离$d2$一定等于$d$** **所以整个过程是一个将每条路径长度“对齐”的过程** 我们进行树形$DP$,从叶子节点到根节点转移。设当前节点是$u$,那么必须保证向上传递时$u$到它的子树内的每个叶子节点距离相等,否则就永远“对不齐”了。 ![](https://img2018.cnblogs.com/blog/1564177/201910/1564177-20191024153003328-1694011439.png)

    就拿上面这个例子来说,假如(dis(a,u)!=dis(b,u)),那么对(u)上面的边((i))无论怎么修改,(a,b)向上的距离是同等增大的,(dis(a,fa))一定不等于(dis(b,fa))(类似于分配律)
    我们为确保答案合法,一定要在(u)节点是保证(dis(u,a)=dis(u,b)),即让它到子树中每个叶子节点距离相等,所以可以改变(j、k)变使他们对齐,只需要都加成(u)到叶子节点距离的最大值即可

    这样做一定能确保最终所有距离相等,且我们在尽可能向上的边(如果再往上就是(i)边,修改不能使答案合法)修改,类似于分配的思想,一定比在下面修改花费的代价更小。(因为在上面(+2)等效于对每条路径(+2),所以在合法的情况下越往上越优)

    在代码实现上,我们先要预处理每个节点到子树中的叶子结点的最大距离(maxx[u]),方便后面对齐操作计算代价使用,在回溯的时候搞一下
    然后再次(dfs)做树形(dp)统计答案即可(事实上可以放一块,但是我懒得写)
    记得开(long long)

    (Code)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define re register
    #define maxn 500010
    #define int long long
    using namespace std;
    inline int read()
    {
    	int x=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    struct Edge{
    	int v,w,nxt;
    }e[maxn<<2];
    int to_l[maxn],tmp[maxn],cnt2,maxx[maxn];
    int max_dis,x,y,z,root,head[maxn],cnt,n,dp[maxn];
    inline void add(int u,int v,int w)
    {
    	e[++cnt].v=v;
    	e[cnt].w=w;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    void dfs1(int u,int fa)
    {
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int ev=e[i].v;
    		if(ev==fa) continue;
    		dfs1(ev,u);
    		maxx[u]=max(maxx[ev]+e[i].w,maxx[u]);
    	}
    }
    void dfs2(int u,int fa)
    {
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int ev=e[i].v;
    		if(ev==fa) continue;
    		dfs2(ev,u);
    		dp[u]+=(maxx[u]-(maxx[ev]+e[i].w));//计算对齐需要的花费 
    		dp[u]+=dp[ev];//不要忘了把子树的答案传递上来 
    	}
    }
    signed main()
    {
    	n=read();
    	root=read();
    	for(re int i=1;i<n;++i)
    	{
    		x=read(),y=read(),z=read();
    		add(x,y,z),add(y,x,z);
    	}
    	dfs1(root,0);//预处理 
    	dfs2(root,0);//DP  和上面的放一个函数里也行
    	printf("%lld
    ",dp[root]);
    	return 0;
    }
    
    
  • 相关阅读:
    有用的Python模块
    Python中for循环搭配else的陷阱
    MySQL实用操作
    Pycharm常用快捷键
    MySQL基础
    HTML基础
    MySQL基础
    HTTP连接管理
    TCP连接的建立和终止
    TCP数据流
  • 原文地址:https://www.cnblogs.com/Liuz8848/p/11732625.html
Copyright © 2011-2022 走看看