Betrayed (树上每点的最长链)树形DP+(回到原点)期望DP

题目

Betrayed(https://ac.nowcoder.com/acm/problem/128934)

题目大意

Mohammad决定提交基于DFS的问题解决方案(深度优先搜索)。这个问题的输入文件包含了许多具有无向边的树,他的代码将在所有这些树上进行测试。
下载输入文件后,他发现他的程序因堆栈溢出而崩溃,因为他必须在提交此问题的超时过期之前非常快地更新代码,所以他决定不从节点1启动DFS,
而是从随机选择的节点启动(所有节点都有相同的被选择概率)。
假设运行该解决方案需要一秒钟的时间来计算每棵树的答案,如果程序崩溃,Mohammad将再花3秒钟来重新运行它,在这种情况下,应该从第一棵树开始重新运行解决方案。
如果Mohammad的递归函数的深度超过K,就会出现堆栈溢出。
在没有堆栈溢出的情况下通过所有树的预期秒数是多少?

输入描述

第一行输入包含一个整数T(1≤ T ≤ 128),即测试用例的数量。
每个测试用例将以包含两个空格 - 分隔整数的行开始,C(1≤ C≤ 20),下载的输入文件的树数,以及K(1≤ K≤ 10^5),这是不会导致堆栈溢出的最大深度。下面的每一个C行代表下载的输入文件中的一个树。每棵树的格式如下:
n a2a3a4…安,(1≤ n≤ 10^5),(1≤ ai≤ n)
其中n是节点数,ai表示ai和i之间的无向边。
它保证在任何给定的树中,如果Mohammad从这些节点启动DFS,那么至少25%的节点不会导致堆栈溢出。

输出描述:

对于每个测试用例打印一个单独的数字,在不获得堆栈溢出的情况下通过所有树的预期秒数,四舍五入到小数点后4位。

输入样例

3
2 2
2 1
3 1 1
3 5
7 3 4 7 1 5 6
6 1 4 2 1 5
5 1 1 5 1
4 3
4 4 1 1
6 1 2 1 1 5
5 4 1 3 4
1

输出样例

2.0000
4.6000
6.5000

思路

对于每一棵树,我们要统计以每个点为根的树的最长深度。树形DP统计最长链和次长链+换根就可以。
现在得到统计每棵树的概率p[i]和不能通过的概率q[i]=(1-p[i])。
dp[i]:从第1棵树到第i棵树的期望时间。
\(dp[i]=dp[i-1]+q[i]*(1)+p[i](1+3+dp[i])\)
我们在第i-1棵树的时候。尝试一次。p[i]成功只用1秒。
有q[i]失败,尝试时间1+重启时间+3+重新从1到i的时间。
化简:dp[i]=(dp[i-1]+1+3*q[i])/(1-q[i]);

#include <bits/stdc++.h>
#define LL long long
using namespace std;

struct Edge{
    int to, w, nxt;
}E[200005];
int head[200005], cut=0;
void AddEdge(int u, int v, int w){
    E[++cut]={v, w, head[u]}; head[u]=cut;
}

int f[200005][2];//以i为根的 最长链 次长链
void dfs(int u, int fa, int ww){
    for(int i=head[u]; i; i=E[i].nxt){
        int x=E[i].to, w=E[i].w;
        if(x==fa) continue;
        dfs(x, u, w);
        if(f[x][0]+w>=f[u][0]) f[u][1]=f[u][0], f[u][0]=f[x][0]+w;
        else if(f[x][0]+w>f[u][1]) f[u][1]=f[x][0]+w;
    }
}

void dfs2(int u, int fa, int ww){
    if(ww>=f[u][0]) f[u][1]=f[u][0], f[u][0]=ww;
    else if(ww>f[u][1]) f[u][1]=ww;

    for(int i=head[u]; i; i=E[i].nxt){
        int x=E[i].to, w=E[i].w;
        if(x==fa) continue;
        if(f[x][0]+w==f[u][0]) dfs2(x, u, f[u][1]+w);
        else dfs2(x, u, f[u][0]+w);
    }
}

int main() {

    int T; scanf("%d", &T);
    while(T--){
        int c, k; scanf("%d%d", &c, &k);
        double p[25]={0}, q[25]={0}, dp[25]={0};

        for(int cc=1; cc<=c; cc++){
            int n; scanf("%d", &n);
            memset(head, 0, sizeof(head)); cut=0;
            memset(f, 0, sizeof(f));

            for(int i=2; i<=n; i++){
                int x; scanf("%d", &x);
                AddEdge(i, x, 1); AddEdge(x, i, 1);
            }
            dfs(1, 0, 0);
            dfs2(1, 0, 0);
            int siz=0;
            for(int i=1; i<=n; i++){
                if(f[i][0]<=k) siz++;
            }
            p[cc]=1.0*siz/n, q[cc]=(1-p[cc]);
        }
        for(int i=1; i<=c; i++){
            dp[i]=(dp[i-1]+1+3*q[i])/(1-q[i]);
        }
        printf("%.4f\n", dp[c]);
    }

    return 0;
}
posted @ 2020-12-07 17:30  liweihang  阅读(192)  评论(0编辑  收藏  举报
Live2D