The Preliminary Contest for ICPC Asia Nanjing 2019 ICPC徐州站网络赛 J Random Access Iterator(树形DP+概率DP)

题目链接:https://nanti.jisuanke.com/t/41392

 

题目大意:每次dfs随机选择儿子中的一个,选择儿子的个数次,问最终能得到正确的树深度的概率

 

题目思路:榜好像歪了啊,感觉这题贼水啊...同校大佬在我们9题后过了三分钟就一发过了这题,直接罚时压我们一头自闭啊...他们过了才发现这题结果来不及写了,有点难受,还是前期做太慢+太信任榜没有积极开题..

正着感觉很难,不知道选哪个小伙子,正难则反,直接把不行的小伙子踢掉,剩下的都是我要的小伙子。哪些小伙子肯定是不要的?bingo,就是那些深度不够的叶子,他们这条链不行的概率是百分百,而哪些深度是最深的叶子,他们不行的概率是0,到他们就稳了,所以我们要通过叶子回去。那么怎么回去呢?首先,简化问题,如果只选一次咋搞?肯定有聪明的小伙子说了,1/son[u]*dp[y]加和,意思是1/儿子个数(选择这个孩子的概率)*这个儿子不行的概率,把所有儿子的这个值加和就等于只选一次凉凉的概率,一共选儿子的个数次,那就直接来个k的幂次就美滋滋了,然后(1-dp[1]+MOD)%MOD就是答案了

 

以下是代码:

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
const ll MAXN = 1e6+5;
const ll MOD = 1e9+7;
vector<ll>v[MAXN];
ll powmod(ll x,ll y){
    ll ans=1;
    for(;y;y>>=1){
        if(y&1)ans=ans*x%MOD;
        x=x*x%MOD;
    }
    return ans;
}
ll n,x,y;
ll dep[MAXN],son[MAXN],maxx;
ll dp[MAXN];
void dfs(ll u,ll fa,ll depp){
    ll len=v[u].size();
    son[u]=0;
    dep[u]=depp;
    maxx=max(maxx,depp);
    ll x=0;
    rep(i,0,len-1){
        ll y=v[u][i];
        if(y==fa)continue;
        son[u]++;
        dfs(y,u,depp+1);
    }
}
void dfs2(ll u,ll fa){
    ll len=v[u].size();
    ll temp=0;
    rep(i,0,len-1){
        ll y=v[u][i];
        if(y==fa)continue;
        dfs2(y,u);
        temp=(temp+powmod(son[u],MOD-2)*dp[y]%MOD)%MOD;
    }
    temp=powmod(temp,son[u]);
    dp[u]=temp;
    if(!son[u]){
        if(dep[u]==maxx){
            dp[u]=0;
        }
        else dp[u]=1;
    }
}
int main()
{
    while(~scanf("%lld",&n))
    {
        memset(dep,0,sizeof(dep));
        memset(dp,0,sizeof(dp));
        maxx=0;
        rep(i,1,n)v[i].clear();
        rep(i,1,n-1){
            scanf("%lld%lld",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        dfs(1,-1,1);
        dfs2(1,-1);
        printf("%lld\n",(1-dp[1]+MOD)%MOD);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值