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}
\]
然后就不会了……
考虑线段树合并(我真的不知道这东西复杂度是对的),每个节点开一个权值线段树。
合并的时候就考虑第一个加数就好了(第二个类似),而第一个加数相当于先继承过来,再乘上一些东西。
这些东西本来是可以在线段树上\(\mathcal O(\log n)\)分治查询的,但是在合并的时候只要用类似cdq的技巧,边合并,边统计即可。
具体来说,就是,现在要合并两个树上节点\(x, y\)的相同线段树区间节点,然后可以个区间划分成左右两块,节点\(x\)的叫做\(x_l\)和\(x_r\),节点\(y\)的叫做\(y_l\)和\(y_r\)。
那么,\(x_l\)对\(y_r\),\(y_r\)对\(x_l\),\(x_r\)对\(y_l\),\(y_l\)对\(x_r\)的影响都是确定的(即严格大于或小于的关系),然后累加这个影响就行了。(具体见实现)
线段树节点上只要维护前缀积的区间和即可(前缀积就相当于继承的意思),并且利用动态开点线段树就可以保证正确的复杂度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300005, mod = 998244353, inv = 796898467;
int n, m, p, tot, ans, v[N], a[N], sz[N], ch[N][2];
int rt[N], lc[N * 20], rc[N * 20], s[N * 20], tag[N * 20];
void add (int x, int y) {
    ch[x][1] = y, swap(ch[x][0], ch[x][1]);
}
void mul (int x, int t) {
    s[x] = 1ll * s[x] * t % mod;
    tag[x] = 1ll * tag[x] * t % mod;
}
void pushdown (int x) {
    if (tag[x] != 1) {
        mul(lc[x], tag[x]);
        mul(rc[x], tag[x]);
        tag[x] = 1;
    }
}
void insert (int &o, int l, int r, int k) {
    if (!o) {
        o = ++tot;
    }
    s[o] = tag[o] = 1;
    if (l == r) {
        return;
    }
    int mid = (l + r) >> 1;
    if (k <= mid) {
        insert(lc[o], l, mid, k);
    } else {
        insert(rc[o], mid + 1, r, k);
    }
}
int merge (int x, int y, ll sumx = 0, ll sumy = 0) {
    if (!x || !y) {
        x ? mul(x, sumy) : mul(y, sumx);
        return x | y;
    }
    pushdown(x), pushdown(y);
    int x0 = s[lc[x]], x1 = s[rc[x]], y0 = s[lc[y]], y1 = s[rc[y]];
    lc[x] = merge(lc[x], lc[y], ((1ll + mod - p) * x1 + sumx) % mod, ((1ll + mod - p) * y1 + sumy) % mod);
    rc[x] = merge(rc[x], rc[y], (1ll * p * x0 + sumx) % mod, (1ll * p * y0 + sumy) % mod);
    s[x] = (s[lc[x]] + s[rc[x]]) % mod;
    return x;
}
int dfs (int x) {
    if (!ch[x][0]) {
        insert(rt[x], 1, m, lower_bound(a + 1, a + m + 1, v[x]) - a);
        return rt[x];
    }
    int rl = dfs(ch[x][0]);
    if (!ch[x][1]) {
        return rl;
    }
    int rr = dfs(ch[x][1]);
    p = v[x];
    return merge(rl, rr);
}
int calc (int o, int l, int r) {
    if (l == r) {
        return 1ll * l * a[l] % mod * s[o] % mod * s[o] % mod;
    }
    pushdown(o);
    int mid = (l + r) >> 1;
    return (calc(lc[o], l, mid) + calc(rc[o], mid + 1, r)) % mod;
}
int main () {
    scanf("%d", &n);
    for (int i = 1, x; i <= n; ++i) {
        scanf("%d", &x);
        if (x) {
            add(x, i);
        }
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &v[i]);
        if (!ch[i][0]) {
            a[++m] = v[i];
        } else {
            v[i] = 1ll * v[i] * inv % mod;
        }
    }
    sort(a + 1, a + m + 1);
    printf("%lld\n",calc(dfs(1), 1, m));
    return 0;
}

原文地址:https://www.cnblogs.com/psimonw/p/11445087.html

时间: 2024-10-03 03:34:20

loj2537. 「PKUWC2018」Minimax的相关文章

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\

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 #2541「PKUWC2018」猎人杀

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

Loj #2542. 「PKUWC2018」随机游走

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

「ZJOI2019」Minimax 搜索(动态dp)

Address loj3044 Solution 考虑对 \(k\in [l-1,r]\) 分别求出有多少个集合 \(S\) 满足 \(w(S)\le k\),记作 \(ans_k\). 先求出 \(1\) 的初始权值 \(W\). 记 \(val(x)\) 表示 \(x\) 的权值.枚举 \(k\),现在对于每个叶子 \(u\),如果 \(u\in S\),那么 \(val(u)\in [u-W,u+W]\),否则 \(val(u)=W\). 我们发现,把叶子节点的权值改成 \(W\) 肯定是

「PKUWC2018」随机游走

题面在这里! 显然你如果直接带一个子集到树上dp的话复杂度是会炸上天的23333. 考虑期望也是可以进行min_max容斥的,也就是: max{S} = ∑ min{T} * (-1) ^( |T|+1 ) ,其中T是S的一个非空子集,max{S}和min{S}分别代表集合中所有条件都被满足的期望时间 和 集合中至少有一个条件被满足的期望时间, 当然对本题来说就是 所有钦定的点都被到过一次的期望时间 和 第一次到某个钦定的点的期望时间.... 发现min非常的好算,对于每个集合直接一次树上dp

loj2541 「PKUWC2018」猎人杀

https://loj.ac/problem/2541 自己是有多菜啊,10天前做的题,当时还是看了题解,还让NicoDafaGood同学给我讲了一下. 而我现在忘得一干二净,一点都想不起来了…… 主要是当时听懂了就打了,没有总结啊. 我们发现,我们设集合$A$的$w$之和是$S_A$ 那么一个集合$A$在1之后死的概率是$\frac{w_1}{S_A+w_1}$. 为什么呢. 虽然每次选下一个会死的人,是从没死的人中选,但是实际上,也可以是所有人中选,如果选到了死了的人就继续选. 记得很久以前

loj#2540. 「PKUWC2018」随机算法

传送门 完了pkuwc咋全是dp怕是要爆零了-- 设\(f(S)\)表示\(S\)的排列数,\(S\)为不能再选的点集(也就是选到独立集里的点和与他们相邻的点),\(mx(S)\)表示\(S\)状态下对应的独立集大小,枚举点\(i\),如果\(i\)不在\(S\)里,分情况考虑,设\(w[i]\)表示点\(i\)以及与之相邻的点,\(T=S|w[i]\),\(sz[S]\)表示二进制\(S\)有多少个\(1\),如果\(mx[T]=mx[S]+1\),那么\[f[T]+=f[S]\times A