BZOJ3566: [SHOI2014]概率充电器

138 篇文章 0 订阅
12 篇文章 0 订阅

这道题貌似我的做法比较奇怪(雾)?

首先互不相关的两件事 x,y 同时发生的概率是 P(x)+P(y)P(x)P(y)

直接考虑怎么算进入充电状态元件的通电个数
因为若一棵子树需要对其他节点产生贡献或其他节点对这棵子树内的点产生贡献,都只能通过树根传递电流,所以设
f1[i] 表示以 i 为根的子树内有电流传导到i的概率
f2[i] 表示若有电流通到 i ,以 i 为根的子树内得到充电的元件的期望

那么对于单独的点 x f1[x] 就是 x 直接充电的概率,f2[x]=1f1[x]
合并两棵树时,若 y x 的孩子,他们之间的边通电的概率为 c
那么根据这条边是否通电分类转移,有
ans+=c(f1[x]f2[y]+f2[x]f1[y])
f1[x]=c(f1[x]+f1[y]f1[x]f1[y])+(1c)f1[x]
因为在 x,y 各自的子树内,计算 f2 的时候只考虑了自身的 f1 ,所以合并的时候要把另一棵树的 f1 考虑进去
f2[x]=c((1f1[x])f2[y]+(1f1[y])f2[x])+(1c)f2[x]
或者按照点分类讨论可以得到
(f2[y](1f1[x]))c+f2[x](1f1[y]c) 两个都是对的

ans 最后还要加上所有点直接充电的概率

分类讨论、互相独立的思想很重要
AC后翻了翻题解,补集转化,父亲和儿子贡献分开的思路值得借鉴

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e8
using namespace std;

inline int read()
{
    char c; int x;
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    return x;
}
inline void up(int &x,const int &y){if(x<y)x=y;}
inline void down(int &x,const int &y){if(x>y)x=y;}
const int maxn = 510000;

struct edge
{
    int y,c,nex;
    edge(){}
    edge(const int &_y,const int &_c,const int &_nex){y=_y;c=_c;nex=_nex;}
}a[maxn<<1]; int fir[maxn],len;
inline void ins(const int &x,const int &y,const int &c)
{a[++len]=edge(y,c,fir[x]);fir[x]=len;}

int n;
long double f1[maxn],f2[maxn],ans;
void dfs(const int x,const int fa)
{
    f2[x]=1-f1[x];
    for(int k=fir[x];k;k=a[k].nex)
    {
        const int y=a[k].y;
        if(y!=fa)
        {
            dfs(y,x);
            long double c=(long double)a[k].c/100.0;
            ans+=f1[x]*c*f2[y]+f2[x]*c*f1[y];
            long double t1=c*(f1[x]+f1[y]-f1[x]*f1[y])+(1-c)*f1[x];
            long double t2=/*(f2[y]*(1-f1[x]))*c+f2[x]*(1-f1[y]*c)*/
                        c*(f2[y]*(1-f1[x])+f2[x]*(1-f1[y]))+(1-c)*f2[x];;
            f1[x]=t1; f2[x]=t2;
        }
    }
}

int main()
{
    memset(fir,0,sizeof fir); len=0;

    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),p=read();
        ins(x,y,p); ins(y,x,p);
    }
    ans=0;
    for(int i=1;i<=n;i++) f1[i]=(long double)read()/100.0,ans+=f1[i];

    dfs(1,0);

    printf("%.6Lf\n",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值