HDU-4219-Randomization?

题目描述

给定一棵\(n\)个节点的树,每条边的权值为\([0,L]\)之间的随机整数,求这棵树两点之间最长距离不超过\(S\)的概率。

Input

第一行三个整数\(n,L,S\)

接下来n-1行,每行两个整数,表示树上的一条边。

  1. \(1 <= T <= 512\)
  2. \(1 <= N <= 64\)
  3. \(1 <= L <= 8, 1 <= S <= 512\)

加强版:

  1. \(T=1\)
  2. \(1 <= N <= 100\)
  3. \(1 <= L <= 10, 1 <= S <= 2000\)

Output

输出树上任意两点距离都不超过\(S\)的概率,误差在\(10^{-6}\)以内都在算正确

Sample Input

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

Sample Output

Case 1: 0.750000
Case 2: 0.500000
Case 3: 0.624832

让我们先来考虑一下未加强的数据该怎么去写?

题目中树上两点最大距离就是树的直径。

因此,对于一个节点,我们需要维护信息是该节点到其子树的最大值的距离的概率。

同时,我们还要把儿子向父亲转移,即维护儿子到另一个儿子的距离的概率。

那么,我们就可以直接树形\(DP\)了。

\(dp[i][j]\)表示节点\(i\)到其子树的最大值为\(j\)的概率。

\(Dis[i]\)表示当前节点的子树到父亲的距离为\(i\)的概率。

\(temp[i]\)进行储存\(dp\)值,因为一个\(dp\)值会被多次利用。

总时间复杂度:\(O(n*S*(S+L))\)

对于这个算法是无法过加强版的。

我们可以发现,这个\(dp\)的时间复杂度主要累积到\(temp\)值的累积上。

并且\(temp\)值的累积一定是连续的一段区间。

于是我们就可以利用前缀和优化,最后再把重复的给减掉。

时间复杂度:\(O(n*S*L)\)

代码如下

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define u64 unsigned long long
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(reg int i=(G).Head[x]; i; i=(G).Nxt[i])

inline int Read() {
    int res = 0, f = 1;
    char c;
    while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
    do res = (res << 3) + (res << 1) + (c ^ 48);
    while (c = getchar(), c >= 48 && c <= 57);
    return f ? res : -res;
}

template<class T>inline bool Min(T &a, T const&b) {
    return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
    return a < b ? a = b, 1 : 0;
}

const int N = 2e3+5, M = 1005, mod = 1e9 + 7;

bool MOP1;

int n,l,s,Case;

struct Link_list {
    int Tot,Head[N],Nxt[M<<1],to[M<<1],cost[M<<1];
    inline void Init(void) {
        Tot=0;
        memset(Head,0,sizeof Head);
    }
    inline void AddEdgepair(int a,int b) {
        to[++Tot]=b,Nxt[Tot]=Head[a],Head[a]=Tot;
        to[++Tot]=a,Nxt[Tot]=Head[b],Head[b]=Tot;
    }
} G;

struct T290 {
    double Dis[N],dp[N][N],temp[N];
    void dfs(int x,int pre) {
        rep(i,1,s)dp[x][i]=0;
        dp[x][0]=1;
        rep(j,0,s)Dis[j]=temp[j]=0;
        erep(i,G,x) {
            int y=G.to[i];
            if(y==pre)continue;
            dfs(y,x);
            rep(j,0,s)Dis[j]=temp[j]=0;
            rep(j,0,l)rep(k,0,s)if(j+k<=s)Dis[j+k]+=dp[y][k]*1.0/(l+1);
            else break;
            rep(j,0,s)rep(k,0,s-j)temp[max((int)k,(int)j)]+=dp[x][k]*Dis[j];
            rep(j,0,s)dp[x][j]=temp[j];
        }
    }
    inline void solve(void) {
        ret(i,1,n) {
            int a=Raed(),b=Read();
            G.AddEdgepair(a,b);
        }
        dfs(1,0);
        double Ans=0;
        rep(i,0,s)Ans+=dp[1][i];
        printf("Case %lld: %.6lf\n",++Case,Ans);
    }
} Pbl;

struct T2100 {
    double Dis[N],dp[N][N],temp[N],Sum1[N],Sum2[N];
    void dfs(int x,int pre) {
        rep(i,1,s)dp[x][i]=0;
        dp[x][0]=1;
        erep(i,G,x) {
            int y=G.to[i];
            if(y==pre)continue;
            dfs(y,x);
            rep(j,0,s)Dis[j]=temp[j]=0;
            rep(j,0,l)rep(k,0,s)if(j+k<=s)Dis[j+k]+=dp[y][k]*1.0/(l+1);
            else break;
            Sum1[0]=dp[x][0];
            Sum2[0]=Dis[0];
            rep(i,1,s) {
                Sum1[i]=Sum1[i-1]+dp[x][i];
                Sum2[i]=Sum2[i-1]+Dis[i];
            }
            rep(j,0,s) {
                int k=min(j,s-j);
                temp[j]+=Sum1[k]*Dis[j];
            }
            rep(j,0,s) {
                int k=min(j,s-j);
                temp[j]+=Sum2[k]*dp[x][j];
            }
            rep(j,0,s)if(j*2<=s)temp[j]-=dp[x][j]*Dis[j];
            rep(j,0,s)dp[x][j]=temp[j];
        }
    }
    inline void solve(void) {
        ret(i,1,n) {
            int a=Raed(),b=Read();
            G.AddEdgepair(a,b);
        }
        dfs(1,0);
        double Ans=0;
        rep(i,0,s)Ans+=dp[1][i];
        printf("Case %lld: %.6lf\n",++Case,Ans);
    }
} P100;

bool MOP2;

inline void _main(void) {
    int T=Read();
    while(T--) {
        G.Init();
        n=Raed(),l=Read(),s=Read();
        P100.solve();
    }
}

signed main() {
#define offline1
#ifdef offline
    freopen("random.in", "r", stdin);
    freopen("random.out", "w", stdout);
    _main();
    fclose(stdin);
    fclose(stdout);
#else
    _main();
#endif
    return 0;
}

转载于:https://www.cnblogs.com/dsjkafdsaf/p/11286743.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值