Kattis - heapsoffun

题意

一棵树,每个点有个取值范围,随机取值(实数),最后树是一个小根堆的概率

题解

取值范围是很大的,无法枚举,这个时候:
我们可以把 f u ( x ) f_u(x) fu(x)定义成: u u u的子树合法且 u u u x x x的概率(准确说是概率密度);
f u ( x ) = 1 b u f_u(x)=\frac{1}{b_u} fu(x)=bu1[叶子节点]

这个是无法直接做的,我们考虑概率分布函数 F u ( x ) = ∫ f u ( x ) F_u(x)=\int f_u(x) Fu(x)=fu(x),表示的是 u u u的子树合法且 u u u取值 ≤ x \leq x x的概率。

知道这个其实就可以尝试做了,但是由于还是有 x x x,而且 x x x显然是较大的,并且主要是通过 f u f_u fu F u ( x ) F_u(x) Fu(x)无法得知如何从孩子节点的状态转移过来(快速转移), u u u ≤ x \leq x x,子树并不知道是多少。

这个时候我们再定义一个函数 G u ( x ) G_u(x) Gu(x):表示合法且 u ≥ x u\geq x ux的概率。
G u ( x ) = F u ( b u ) − F u ( x ) G_u(x)=F_u(b_u)-F_u(x) Gu(x)=Fu(bu)Fu(x).
显然有: f u ( x ) = 1 b u ∗ ∏ G s o n ( x ) f_u(x)=\frac{1}{b_u}*\prod G_{son}(x) fu(x)=bu1Gson(x)保证孩子节点都比 f u f_u fu

最后结果是 G r o o t ( 0 ) G_{root}(0) Groot(0).
注意一个细节,我们计算的时候 x x x不应该超过下面的最小(最大值),否则会出现错误,所以我们先计算出 f u ( x ) → F u ( x ) f_u(x)\to F_u(x) fu(x)Fu(x),计算 G u ( x ) G_u(x) Gu(x)的时候,要考虑 x ≤ min ⁡ ( max ⁡ ) x \leq \min(\max) xmin(max),因为这个时候需要代值,其他时候可以直接计算多项式,而不是先代值,这样就会比较简单。
显然这些函数都是多项式(积分得到的),所以模拟多项式即可。

int n,a[maxn],b[maxn];
int inv[maxn];
vector<int>G[maxn];
//inv[0]=inv[1]=1;
//for(int i=2;i<=300;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
struct poly{
    int a[maxn],deg;
    poly(){memset(a,0,sizeof(a));}
    poly(int x){memset(a,0,sizeof a),a[0]=x,deg=1;}
    int&operator[](int x){return a[x];}
    void inc(int&a,int b){a+=b-mod,a+=a>>31&mod;}//*2
};
void inc(int&a,int b){a+=b-mod,a+=a>>31&mod;}
int sub(int a,int b){return a-=b,a+(a>>31&mod);}
int mul(int a,int b){return 1ll*a*b%mod;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
poly sub(poly f,poly g){
    poly h;h.deg=std::max(f.deg,g.deg);
    for(int i=0;i<h.deg;++i) h[i]=sub(f[i],g[i]);
    return h;
}
poly Int(poly f){
    poly g;g.deg=f.deg+1;
    for(int i=0;i<f.deg;++i) g[i+1]=mul(f[i],inv[i+1]);
    return g;
}
poly mul(poly f,poly g){
    poly h;h.deg=f.deg+g.deg-1;
    for(int i=0;i<f.deg;++i) for(int j=0;j<g.deg;++j) inc(h[i+j],mul(f[i],g[j]));
    return h;
}

int cal(poly f,int x){
    int s=0;
    for(int i=0,t=1;i<f.deg;++i,t=mul(t,x)) inc(s,mul(t,f[i]));
    return s;
}

poly dfs(int u,int f){
    poly res=poly(pow(b[u],mod-2));
    a[u]=b[u];
    for(int i=0,v;i<G[u].size();i++){
        v=G[u][i];
        if(v==f)continue;
        res=mul(res,dfs(v,u));//求fu
        a[u]=min(a[u],a[v]);
    }
    res=Int(res);//求Fu
    res=sub(poly(cal(res,a[u])),res);
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值