zoukankan      html  css  js  c++  java
  • 【UOJ105】[APIO2014] Beads and wires(换根DP)

    点此看题面

    • 有两种不同的方式来生成一棵树:
      • 将一个新点与一个旧点用红线连接。
      • 把一条红线断开,将一个新点与这两个端点用蓝线连接。
    • 规定一棵树的权值为蓝线权值总和。
    • 给出最后生成的树(不给出颜色),求可能的最大权值。
    • (nle2 imes10^5)

    推性质

    蓝线必然两两成对,不妨把一对蓝线中间的点称为其中点

    显然,一个点不可能成为两对蓝线的中点。

    而考虑两条连续的边只有儿子-父亲-另一个儿子儿子-父亲-父亲的父亲两种情况。

    然而这样的情况并不是都合法的,典型的反例:

    这里的边可以分为三对儿子-父亲-另一个儿子的情况,但是并不能生成这样的树。(自己去试试就明白了)

    所以我们需要转换思路。

    一般的(DP)转移

    考虑我们必然能找到一个点,在以其为根时,只取儿子-父亲-父亲的父亲的蓝色边对能够得到最优解。

    先思考不换根时的(DP)方程,可以设(f(x),g(x))分别表示不取/取(x)为中点时的答案。

    转移方程:

    [f(x)=summax{f(y),g(y)+w_{x,y}}\g(x)=max{(f(x)-max{f(y),g(y)+w_{x,y}})+f(y)+w_{x,y}} ]

    第一个转移是显然的,第二个转移就是考虑枚举儿子-父亲的边,删掉该儿子原先的贡献强制其不为中点,然后算上这条边的权值(当前点到父节点权值不算在(g(x))内)。

    换根(DP)

    然后考虑换根,用(A,B)表示父节点传来的(f,g)值。

    显然根节点不可能作为中点,因此只需考虑(f(x)+max{A,B+w_{x,fa}})对答案的贡献。

    换根时得到的新(A,B)应该这样计算:

    [ exttt{new} A=(f(x)-max{f(y),g(y)+w_{x,y}})+max{A,B+w_{x,fa}}\ exttt{new} B=max{g(x)+max{A,B+w_{x,fa}},f(x)+A+w_{x,fa}}-max{f(y),g(y)+w_{x,y}} ]

    注意,( exttt{new}B)的计算中,如果(g(x))是由当前枚举的子节点(y)转移得到的,需要改为次大值(h(x))

    其实这两个式子就是对于先前转移方程的稍加变化而已,只是要注意减去当前子节点的贡献,可以自己理解。

    代码:(O(n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
    using namespace std;
    int n,ee,lnk[N+5];struct edge {int to,nxt,v;}e[N<<1];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    int f[N+5],g[N+5],h[N+5],s[N+5],tmp[N+5];I void dfs1(CI x=1,CI lst=0)//一般DP转移
    {
    	RI i,t;for(i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    		(dfs1(e[i].to,x),f[x]+=(tmp[e[i].to]=max(f[e[i].to],g[e[i].to]+e[i].v)));//不为中点
    	for(g[x]=h[x]=-1e9,i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//作为中点
    	(
    		t=f[x]-tmp[e[i].to]+f[e[i].to]+e[i].v,//求出当前子节点转移过来的贡献
    		t>g[x]?(h[x]=g[x],g[x]=t,s[x]=e[i].to):h[x]=max(h[x],t)//维护最大值、次大值
    	);
    }
    int ans;I void dfs2(CI x=1,CI lst=0,CI A=0,CI B=-1e9,CI pre=-1e9)//换根DP
    {
    	ans=max(ans,f[x]+max(A,B+pre));//更新ans
    	for(RI i=lnk[x],a,b;i;i=e[i].nxt) e[i].to^lst&&
    	(
    		b=max((s[x]^e[i].to?g[x]:h[x])+max(A,B+pre),f[x]+A+pre)-tmp[e[i].to],
    		a=(f[x]-tmp[e[i].to])+max(A,B+pre),dfs2(e[i].to,x,a,b,e[i].v),0//求出newA,newB实现换根
    	);
    }
    int main()
    {
    	RI i,x,y,z;for(F.read(n),i=1;i^n;++i) F.read(x,y,z),add(x,y,z),add(y,x,z);
    	return dfs1(),dfs2(),printf("%d
    ",ans),0;
    }
    
  • 相关阅读:
    如何得到数据绑定的树节点的父节点
    ImageBrush中的图片如何加载到到MemoryStream
    C#中动态加载和卸载DLL
    SetProcessWorkingSetSize减少内存占用
    wpf中如何改变Listbox选中项的颜色
    怎样把Visual Studio与Perforce关联起来
    在WPF里面如何使用FolderBrowserDialog
    关于WPF的ComboBox中Items太多而导致加载过慢的问题(转载)
    得到系统中所有正打开的文件
    把ResourceDictionary保存为文件,从外部xaml文件加载ResourceDictionary
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/UOJ105.html
Copyright © 2011-2022 走看看