zoukankan      html  css  js  c++  java
  • CF1060F Shrinking Tree

    http://codeforces.com/problemset/problem/1060/F

    题解

    仙题。

    参考:https://www.cnblogs.com/Mr-Spade/p/9747399.html

    首先我们可以枚举每个点作为最后保留的节点,以它作为根节点进行树形(dp)

    注意到我们删去一条边可以看做把边权弄成0,这样根节点可以在边权为0的边上随意走动。

    我们设计一个状态:(dp[u][i])表示在以(u)为根的子树中,当根节点走到(u)时,子树内还剩下(i)条边的方案数。

    这样相当于倒着推,那么最后的答案就是根节点刚进入这棵树的状态,就是(dp[rot][n-1])

    那么我们接下来要考虑子树的(dp)值合并。

    我们枚举了两个状态(dp[u][i])(dp[v][j])

    我们发现这样不太够,我们还需要知道从(u)走到(v)的时候还有多少条边,我们令它为(k)

    然后我们考虑概率是多少。

    首先这个(k)肯定不能大于(j)

    (k=j)时,说明(u-v)这条边在(u)来之前就已经被干掉了,那么这条边的删除时间我们可以在(v)子树里的任意一条边的删除时间中插入,并且这条边的删除对根是没有影响的,所以就是((size[v]-j)*1)

    (k<j)时,相当于是先到(u),然后(v)中的一些边被删除了,然后再去删除(u-v),这时需要考虑概率了,只有一半的概率会被选中,所以系数是(0.5)

    最后我们再考虑方案数。

    当前两颗子树还剩下(i)(j)条边,这两组边删去是互不影响的,所以这样的方案数就是插板法,注意已经删去的(size[u]-i-1)条边和(size[v]-j)也是互不影响的,也要用插板法算一下。

    代码

    #include<bits/stdc++.h>
    #define N 52
    #define mm make_pair
    using namespace std;
    typedef long double ll;
    int head[N],tot,size[N],n;
    ll c[N][N],dp[N][N],tmp[N],g[N];
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
    	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f?-x:x;
    }
    struct edge{
    	int n,to;
    }e[N<<1];
    inline ll calc(int n,int m){return c[n][m];}
    inline ll path(int n,int m){return c[n+m][n];}
    inline void add(int u,int v){
    	e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
    }
    void dfs(int u,int fa){
      dp[u][0]=1;size[u]=1;
      for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
      	int v=e[i].to;
      	dfs(v,u);
      	for(int i=0;i<=size[v];++i){
      		g[i]=0;
      		for(int j=0;j<i;++j)g[i]+=0.5*dp[v][j];
      		g[i]+=(size[v]-i)*dp[v][i];
      	} 
      	for(int j=0;j<size[u];++j)
      		for(int k=0;k<=size[v];++k)
      			tmp[j+k]+=dp[u][j]*g[k]*path(j,k)*path(size[u]-1-j,size[v]-k);
      	for(int j=0;j<=size[u]+size[v];++j)dp[u][j]=tmp[j],tmp[j]=0;
      	size[u]+=size[v];
      }
    }
    int main(){
        n=rd();
        for(int i=0;i<=n;++i){
        	c[i][0]=1;
        	for(int j=1;j<=i;++j)c[i][j]=c[i-1][j-1]+c[i-1][j];
        }
        int u,v;
        for(int i=1;i<n;++i){
        	u=rd();v=rd();
        	add(u,v);add(v,u);
        }
        ll xx=1;
        for(int i=1;i<n;++i)xx=xx*i;
        for(int i=1;i<=n;++i){
          memset(dp,0,sizeof(dp));
          dfs(i,0);
          double x=dp[i][n-1]/xx;
          printf("%.10lf
    ",x);
        }
        return 0;
    }
    
    
  • 相关阅读:
    Kafka 再均衡监听器示例
    Spring boot中异步线程池
    【Java&Go并发编程系列】4.等待一组并发任务完成——CountDownLatch VS sync.WaitGroup
    Redis常用命令对应到Redisson对象操作
    Redisson教程
    Redisson官方文档
    Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数
    防止XSS脚本注入-前端vue、后端springboot
    在Intellij IDEA中使用Debug
    appium 处理webview
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/11037894.html
Copyright © 2011-2022 走看看