[JZOJ5621]【NOI2018模拟4.1】反攻

本文介绍了一种基于树形结构的动态规划算法,用于解决节点间相互影响的问题。通过定义两个状态变量Fi和Gi,分别表示只考虑节点i的子树时i不被感化的概率和i不受其父节点影响的概率。利用DFS进行状态转移,实现O(N)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里写图片描述
这里写图片描述

Solution

对每一个点单独算贡献
先定一个树根
Fi表示只考虑i的子树,i不会被感化的概率

容易写出方程为Fi=(1Pi)json[i](1qi,j×(1Fj))

再设Gi表示i不会被它父亲感化的概率,设它父亲为x
容易写出方程为Gi=1qi,j×(1Gx+Gx×(1Fx))

但是如果这样计算G,后面的Fx就会多出来i这个点传上去的影响,所以要除掉
Gi=1qi,j×(1Gx+Gx×(1Fx1qx,i×(1Fi)))

注意分母为0不能除
两边DFS解决
时空复杂度O(N)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 200005
using namespace std;
double f[N],g[N],pr[2*N],fr[N],vl[N];
int fs[N],nt[2*N],dt[2*N],n,m,num;
void link(int x,int y,int z)
{
    nt[++m]=fs[x];
    dt[fs[x]=m]=y;
    pr[m]=z/100.0;
}
void dfs(int k,int fa)
{
    f[k]=1-vl[k];
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa)
        {
            dfs(p,k);
            f[k]*=1-pr[i]*(1-f[p]);
        }
        else fr[k]=pr[i];
    }
}
void dp(int k,int fa)
{
    if(k==1) g[k]=1;
    else 
    {
        double p=f[fa];
        if(1-fr[k]*(1-f[k])!=0) p/=(1-fr[k]*(1-f[k]));
        g[k]=1-fr[k]*((1-g[fa])+g[fa]*(1-p));
    }
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa) dp(p,k);
    }
}
int main()
{
    cin>>num>>n;
    fo(i,1,n-1)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        link(x,y,z),link(y,x,z);
    }
    fo(i,1,n) 
    {
        int x;
        scanf("%d",&x);
        vl[i]=x/100.0;
    }
    dfs(1,0);
    dp(1,0);
    double v=0;
    fo(i,1,n) v+=f[i]*g[i];
    printf("%.6lf\n",v);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值