loj2537 「PKUWC2018」Minimax 【概率 + 线段树合并】

题目链接

loj2537

题解

观察题目的式子似乎没有什么意义,我们考虑计算出每一种权值的概率

先离散化一下权值

显然可以设一个\(dp\),设\(f[i][j]\)表示\(i\)节点权值为\(j\)的概率

如果\(i\)是叶节点显然

如果\(i\)只有一个儿子直接继承即可

如果\(i\)有两个儿子,对于儿子\(x\),设另一个儿子为\(y\)

则有

\[f[i][j] += f[x][j](1 - p_i)\sum\limits_{k > j}f[r][k] + f[x][j]p_i\sum\limits_{k < j}f[r][k]\]

直接转移是\(O(n^2)\)的,发现每个节点都有\(O(n)\)个位置需要转移

考虑优化,可以考虑线段树合并

对于一个子树中的权值\(x\),我们记另一棵子树比它大的概率为\(maxa\),

则\(x\)的概率要乘上\(maxa(1 - p_i) + (1 - maxa)p_i = maxa + p_i - 2p_imaxa\)

所以我们在线段树合并过程中,优先合并右子树,并更新两棵子树的\(maxa\)与\(maxb\),就可以在合并过程中转移了

复杂度\(O(nlogn)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 300005,maxm = 8000005,INF = 1000000000,P = 998244353;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int n,Ls[maxn],Rs[maxn],b[maxn],N,v10000;
int rt[maxn],sum[maxm],ls[maxm],rs[maxm],tag[maxm],cnt;
int p[maxn],maxa,maxb;
inline int qpow(int a,int b){
    int re = 1;
    for (; b; b >>= 1,a = 1ll * a * a % P)
        if (b & 1) re = 1ll * re * a % P;
    return re;
}
inline void pd(int u){
    if (tag[u] > 1){
        sum[ls[u]] = 1ll * sum[ls[u]] * tag[u] % P;
        sum[rs[u]] = 1ll * sum[rs[u]] * tag[u] % P;
        tag[ls[u]] = 1ll * tag[ls[u]] * tag[u] % P;
        tag[rs[u]] = 1ll * tag[rs[u]] * tag[u] % P;
        tag[u] = 1;
    }
}
void modify(int& u,int l,int r,int pos){
    u = ++cnt; sum[u] = tag[u] = 1;
    if (l == r) return;
    int mid = l + r >> 1;
    if (mid >= pos) modify(ls[u],l,mid,pos);
    else modify(rs[u],mid + 1,r,pos);
}
int merge(int u,int v,int p){
    if (!u && !v) return 0;
    if (!u){
        maxb = (maxb + sum[v]) % P;
        int tmp;
        tmp = (((maxa + p) % P - 2ll * p * maxa % P) % P + P) % P;
        sum[v] = 1ll * sum[v] * tmp % P;
        tag[v] = 1ll * tag[v] * tmp % P;
        return v;
    }
    if (!v){
        maxa = (maxa + sum[u]) % P;
        int tmp;
        tmp = (((maxb + p) % P - 2ll * p  * maxb % P) % P + P) % P;
        sum[u] = 1ll * sum[u] * tmp % P;
        tag[u] = 1ll * tag[u] * tmp % P;
        return u;
    }
    pd(u); pd(v);
    int t = ++cnt; tag[t] = 1;
    rs[t] = merge(rs[u],rs[v],p);
    ls[t] = merge(ls[u],ls[v],p);
    sum[t] = (sum[ls[t]] + sum[rs[t]]) % P;
    return t;
}
void dfs(int u){
    if (!Ls[u]) modify(rt[u],1,N,p[u]);
    else if (!Rs[u]) dfs(Ls[u]),rt[u] = rt[Ls[u]];
    else {
        dfs(Ls[u]); dfs(Rs[u]);
        maxa = maxb = 0;
        rt[u] = merge(rt[Ls[u]],rt[Rs[u]],p[u]);
    }
}
int ans;
void cal(int u,int l,int r){
    if (l == r) {ans = (ans + 1ll * l * b[l] % P * sum[u] % P * sum[u] % P) % P;return;}
    pd(u);
    int mid = l + r >> 1;
    cal(ls[u],l,mid);
    cal(rs[u],mid + 1,r);
}
int main(){
    n = read(); read(); int x; v10000 = qpow(10000,P - 2);
    for (int i = 2; i <= n; i++){
        x = read();
        if (!Ls[x]) Ls[x] = i;
        else Rs[x] = i;
    }
    for (int i = 1; i <= n; i++){
        p[i] = read();
        if (!Ls[i]) b[++N] = p[i];
        else p[i] = 1ll * p[i] * v10000 % P;
    }
    sort(b + 1,b + 1 + N);
    for (int i = 1; i <= n; i++)
        if (!Ls[i]) p[i] = lower_bound(b + 1,b + 1 + N,p[i]) - b;
    dfs(1);
    cal(rt[1],1,N);
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9215258.html

时间: 2024-11-07 20:29:15

loj2537 「PKUWC2018」Minimax 【概率 + 线段树合并】的相关文章

loj2537. 「PKUWC2018」Minimax

题意 略. 题解 首先设\(f_{x, c}\)表示以\(x\)为根的子树内,最终取到了\(c\)的概率.可以列出转移方程(假设有两个孩子\(u, v\)) \[ \begin{aligned} f_{x, c} = & f_{u, c} * (p * v子树中最终权值小于c的概率 + (1 - p) * v子树中最终权值大于c的概率) \+ & f_{v, c} * (p * u子树中最终权值小于c的概率 + (1 - p) * u子树中最终权值大于c的概率) \\end{aligned

loj#2537. 「PKUWC2018」Minimax

传送门 感觉我去pkuwc好像只有爆零的份-- 设\(f_{u,i}\)表示\(u\)取到\(i\)的概率,那么有如下转移 \[f_{u,i}=f_{ls,i}(p_u\sum_{j<i}f_{rs,j}+(1-p_u)\sum_{j>i}f_{rs,j})+\\f_{rs,i}(p_u\sum_{j<i}f_{ls,j}+(1-p_u)\sum_{j>i}f_{ls,j})\] 然后用线段树合并即可,最后在根节点的线段树上\(dfs\)统计答案 //minamoto #inclu

「PKUWC2018」Minimax

传送门 Solution 发现叶子节点的值都不样,所以可以线段树合并. 然后因为我们要维护一个后缀,所以我们先合并右儿子,在合并左儿子 Code? //2019.1.14 8:59~10:15 PaperCloud #include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) inline int read() {

Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 $998244353 $ 取模. 输入格式 第一行三个正整数 \(n,Q,x\). 接下来 \(

Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀 题目链接 好巧妙的题! 游戏过程中,概率的分母一直在变化,所以就非常的不可做. 所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效.这样与原问题是等价的. 证明: 设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\). 攻击到未被杀死的猎人\(i\)的概率为\(P\). 则根据题意\(P=\frac{w_i}{sum-kill}\). 问题转化后:

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include