zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:tree(DP)

    题目传送门(内部题57)


    输入格式

    第一行包含一个数:$n$表示树的节点数。
    接下来$n-1$行,每行包含两个数:$u,v$表示无根树的一条边。


    输出格式

    输出$n$行,第$i$行包含一个浮点数,保留三位小数,表示第$i$号点第一次访问的期望时间。


    样例

    样例输入:

    3
    1 2
    2 3

    样例输出:

    1.000
    2.000
    5.000


    数据范围与提示

    样例解释:

    样例解释:容易分析出,所有可能情况下,到达$1$号点和$2$号点的时间都分别是:$1$和$2$,我们考虑$3$号点的到达时间,所有可能的过程:$12(12)*3$,表示先到$1$号店,再到$2$号点,然后重复任意次$1$、$2$(可以是$0$次),最后到达$3$。
    对于$12(12)^i3$这个具体过程来说(表示中间经过$i$次$1$、$2$),到达$3$号点的时间是$t_i=2(i+1)+1$,这个随机过程的概率是$p_i={(frac{1}{2})}^{(i+1)}$,期望的时间是$E(u=3)=sum limits_{i=0}^{infty}t_ip_i=5$,故到达$3$号点的期望时刻为$5$。

    数据范围:

    对于$10\%$的数据,$1leqslant 10$,保证每个点的度不超过$2$;
    对于另外$20\%$的数据,$1leqslant nleqslant {10}^5$,保证每个点的度不超过$2$;
    对于另外$20\%$的数据,$1leqslant nleqslant 100$;
    对于$100\%$的数据,$1leqslant nleqslant {10}^5$。


    题解

    考虑$DP$,设$dp[i]$表示到达$i$点的期望时间。

    你可能会很容易的推出来一个式子:

    $$dp[u]=dp[fa]+2(n-size[u])-1$$

    然后你会发现没有小数,删掉它交暴力。

    这就是我的考试全过程……

    然而,三位小数就是逗我玩的……

    无语……

    下面讲一下推导:

    考虑一个随机过程,第一次走到$u$号点的时间可以分成两部分,第一部分是从$1$号点随机游走第一次走到$u$的父亲$p$的时间,第二部分是从$p$开始走,第一次走到$u$的时间,由期望的线性性,第一次走到$u$的时间期望等于这两部分期望的和。第一部分是一个子问题,我们考虑怎么解决第二部分,我们把这个问题变成一棵树(并且根节点脑袋上也有一条边),从根节点开始随机游走,走出这棵树期望的时间,我们用$x_u$表示这个期望,我们对$u$的子树中的点也类似地定义$x_v$,这样我们可以列出关系式:

    $$x_u=frac{(1+sum limits_{v}(x_u+x_v+1))}{d}$$

    其中$d$是$u$的度数(包括那根天线),这个关系是中的第一个$1$表示直接向上走,后面那个扩后中的三部分,那个$1$表示从$u$走向$v$,$x_v$表示从$v$走回来期望时间, 表示这个时候继续走,走出去还需要花的时间。因为是等概率,所以直接乘以$frac{1}{d}$这个概率即可。化简后是:

    $$x_u=d+sum limits_{v}x_v$$

    即$x_u$等于$u$这棵子树的所有节点度的和,考虑到除了那根天线之外,所有的边对度的贡献为$2$,所以:

    $$x_u=2size[u]+1$$

    这样,子问题就有了一个简单的答案了。我们回到原问题,用$dp[u]$表示第一次走到$u$的期望时间,用$fa$表示$u$的父亲,有:

    $$dp[u]=dp[fa]+2(n-size[u])-1$$

    时间复杂度:$Theta(n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int nxt,to;}e[200001];
    int head[100001],cnt;
    int n;
    bool vis[100001];
    int size[100001];
    long long dp[100001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void dfs1(int x)
    {
    	vis[x]=1;
    	size[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!vis[e[i].to])
    		{
    			dfs1(e[i].to);
    			size[x]+=size[e[i].to];
    		}
    }
    void dfs2(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!vis[e[i].to])
    		{
    			dp[e[i].to]=dp[x]+2*(n-size[e[i].to])-1;
    			dfs2(e[i].to);
    		}
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);add(y,x);
    	}
    	dfs1(1);
    	memset(vis,0,sizeof(vis));
    	dp[1]=1;
    	dfs2(1);
    	for(int i=1;i<=n;i++)
    		printf("%.3lf
    ",(double)dp[i]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    poj 2352 Stars (树状数组)
    一.C语言:关键字、标识符和注释
    运算符:三目运算符,运算符优先级,sizeof,自增自减,取余
    ios app 上架AppStore
    为ios app添加广告条
    谓词
    正则表达式
    日期处理
    第一次往github上传文件步骤
    codeforce 375_2_b_c
  • 原文地址:https://www.cnblogs.com/wzc521/p/11586404.html
Copyright © 2011-2022 走看看