zoukankan      html  css  js  c++  java
  • 【2019.8.20 NOIP模拟赛 T2】小B的树(tree)(树形DP)

    树形(DP)

    考虑设(f_{i,j,k})表示在(i)的子树内,从(i)向下的最长链长度为(j)(i)子树内直径长度为(k)的概率。

    然后我们就能发现这个东西直接转移是几乎不可能的。

    所以我们在转移时要开个辅助数组(s_{op,x,y,k}),其中(op)用于滚存,表示最长链为(x),次长链为(y),子节点子树内直径长度小于等于(k)的概率。

    然后我们只要枚举子节点,再枚举子节点子树内的链长,就可以采用刷表法简便地(DP)转移了。

    这样看似(O(n^5)),但如果你采用了记(Size)优化转移上界的方法,根据树上背包的复杂度,这里应该是(O(n^4))的 。

    最后我们更新(f_{x,i,max(i+j,k)})加上(s_{op,i,j,k}-s_{op,i,j,k-1})

    代码

    #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 60
    #define DB double
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    class DpSolver
    {
    	private:
    		int g[N+5],l[N+5];DB f[N+5][2*N+5][2*N+5],s[2][2*N+5][2*N+5][2*N+5];
    		I void DP(CI x,CI lst)
    		{
    			RI i,j,k,p,q,op;DB t,v;
    			for(g[x]=1,i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(DP(e[i].to,x),g[x]+=g[e[i].to]);//DP子节点
    			for(i=0;i<=2*g[x];++i) s[0][0][0][i]=1;//初始化
    			for(op=0,i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst)//枚举子节点
    			{
    				for(j=0;j<=l[e[i].to];++j) for(k=1;k<=2*g[x];++k) f[e[i].to][j][k]+=f[e[i].to][j][k-1];//处理前缀和,方便转移
    				for(op^=1,j=0;j<=l[x];++j) for(k=0;k<=j;++k) for(p=0;p<=2*g[x];++p)//枚举当前状态
    				{
    					t=s[op^1][j][k][p],s[op^1][j][k][p]=0;//记下当前状态,注意清空原先数组
    					for(q=0;q<=l[e[i].to];++q) v=0.5*t*f[e[i].to][q][p],//枚举子树内最长链
    						q+1>j?(s[op][q+1][j][p]+=v):(q+1>k?s[op][j][q+1][p]+=v:s[op][j][k][p]+=v),//如果这条边长度为1
    						q+2>j?(s[op][q+2][j][p]+=v):(q+2>k?s[op][j][q+2][p]+=v:s[op][j][k][p]+=v);//如果这条边长度为2
    				}Gmax(l[x],l[e[i].to]+2);
    			}
    			for(i=0;i<=l[x];++i) for(j=0;j<=i;++j) for(k=2*g[x];~k;--k)//枚举状态
    				f[x][i][max(i+j,k)]+=s[op][i][j][k]-(k?s[op][i][j][k-1]:0),s[op][i][j][k]=0;//将状态更新到f数组中,并清空
    		}
    	public:
    		I void Solve()
    		{
    			RI i,j;DB ans=0;DP(1,0);//树形DP
    			for(i=0;i<=l[1];++i) for(j=i;j<=2*g[1];++j) ans+=f[1][i][j]*j;//统计答案
    			printf("%.8lf",ans);//输出答案
    		}
    }D;
    int main()
    {
    	freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
    	RI i,x,y;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);//读入建边
    	return D.Solve(),0;
    }
    
  • 相关阅读:
    Weblogic学习笔记
    Shiro权限使用
    Shiro权限框架使用总结
    支付宝接口文档说明
    代码模拟实现十六进制转二进制
    代码模拟实现十六进制转换十进制
    四种内部类详细解释和代码示例
    Struts2_struts.xml写法和用法例子
    spring一些方法和用法例子
    Hibernate一些_方法_@注解_代码示例
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Contest20190820T2.html
Copyright © 2011-2022 走看看