[CF895E] Eyes Closed(线段树,期望)

Desctiption

传送门:Portal

大致题意: 给你一个序列, 支持两种操作:

  1. 1 l1 r1 l2 y2 在\([l1, r1]\)随机选择一个数a, \([l2, r2]\) 内随机选择一个数b, 交换a, b.
  2. 2 l r 询问一个区间的期望.

\[
n \leq 200000; a_i \leq 1e9
\]

Solution

根据==期望线性性==,只需要维护出==每个位置元素值的期望==就可以.

==区间操作期望==问题的经典套路是==维护出每个位置的期望==.

考虑一个数字\(a_i\)

他有\(\frac{len - 1}{len}\) 的概率保持原数, 否则,根据全期望公式, 它会变成\(E(b_i) = \sum\frac{b_i}{lenB}\)

那么有:
\[
A_i = \frac{(lenA - 1)A_i + \sum \frac{b_i}{lenB}}{lenA}
\]
\(b_i\)也同理.

所以我们只需要维护一个区间加/乘, 区间求和的线段树就可以了.

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
    for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

const int Maxn = 200009;
int a[Maxn];

namespace SGMTtree {
    double tree[Maxn << 3], addTag[Maxn << 3], mulTag[Maxn << 3];
#define lc(x) ((x) << 1)
#define rc(x) (((x) << 1) | 1)
#define ls rt << 1, l, mid
#define rs (rt << 1) | 1, mid + 1, r
    void setAdd(int root, int l, int r, double v) {
        tree[root] += v * (r - l + 1.0); addTag[root] += v;
    }
    void setMul(int root, int l, int r, double v) {
        tree[root] *= v;
        mulTag[root] *= v; addTag[root] *= v;
    }
    void (*setTag)(int, int, int, double);
    void set(int p) {
        if(p == 1) setTag = setAdd;
        if(p == 2) setTag = setMul;
    }

    void pushdown(int rt, int l, int r) {
        int mid = (l + r) >> 1;
        if (mulTag[rt] != 1.0) {
            setMul(ls, mulTag[rt]), setMul(rs, mulTag[rt]);
            mulTag[rt] = 1;
        }
        if (addTag[rt] != 0.0) {
            setAdd(ls, addTag[rt]); setAdd(rs, addTag[rt]);
            addTag[rt] = 0;
        }
    }
    void pushup(int rt) { tree[rt] = tree[lc(rt)] + tree[rc(rt)]; }

    void build(int rt, int l, int r) {
        addTag[rt] = 0, mulTag[rt] = 1;
        if (l == r) {
            tree[rt] = a[l];
            return ;
        }
        int mid = (l + r) >> 1;
        build(ls), build(rs);
        pushup(rt);
    }
    void modify(int rt, int l, int r, int p, int q, double v) {
        if (p > q) return ;
        if (p <= l && r <= q) {
            setTag(rt, l, r, v);
            return ;
        }
        int mid = (l + r) >> 1; pushdown(rt, l, r);
        if (q <= mid) modify(ls, p, q, v);
        else if (p > mid) modify(rs, p, q, v);
        else modify(ls, p, q, v), modify(rs, p, q, v);
        pushup(rt);
    }
    double query(int rt, int l, int r, int p, int q) {
        if (p <= l && r <= q) return tree[rt];
        int mid = (l + r) >> 1; pushdown(rt, l, r);
        if (q <= mid) return query(ls, p, q);
        else if (p > mid) return query(rs, p, q);
        else return query(ls, p, q) + query(rs, p, q);
    }
#undef lc
#undef rc
#undef ls
#undef rs
};

int n, q;

void init() {
    n = read(); q = read();
    rep (i, 1, n) a[i] = read();
    SGMTtree :: build(1, 1, n);
}

void solve() {
    rep (i, 1, q) {
        int opt = read();
        if (opt == 1) {
            int l1 = read(), r1 = read(), l2 = read(), r2 = read();
            double c = r1 - l1 + 1.0, d = r2 - l2 + 1.0, tmp1 = SGMTtree :: query(1, 1, n, l1, r1), tmp2 = SGMTtree :: query(1, 1, n, l2, r2);
            SGMTtree :: set(2), SGMTtree :: modify(1, 1, n, l1, r1, c - 1.0);
            SGMTtree :: set(2), SGMTtree :: modify(1, 1, n, l2, r2, d - 1.0);
            SGMTtree :: set(1), SGMTtree :: modify(1, 1, n, l1, r1, tmp2 / d);
            SGMTtree :: set(1), SGMTtree :: modify(1, 1, n, l2, r2, tmp1 / c);
            SGMTtree :: set(2), SGMTtree :: modify(1, 1, n, l1, r1, 1 / c);
            SGMTtree :: set(2), SGMTtree :: modify(1, 1, n, l2, r2, 1 / d);
        }
        if (opt == 2) {
            int l = read(), r = read();
            printf("%.7lf\n", SGMTtree :: query(1, 1, n, l, r));
        }
    }
}

int main() {
//  freopen("CF895E.in", "r", stdin);
//  freopen("CF895E.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}

调试:

线段树函数指针必须要用namespace.

原文地址:https://www.cnblogs.com/qrsikno/p/11511080.html

时间: 2024-08-28 05:43:44

[CF895E] Eyes Closed(线段树,期望)的相关文章

BZOJ 2752: [HAOI2012]高速公路(road) [线段树 期望]

2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1219  Solved: 446[Submit][Status][Discuss] Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1

JZYZOJ1527 [haoi2012]高速公路 线段树 期望

http://172.20.6.3/Problem_Show.asp?id=1527 日常线段树的pushdown写挂,果然每次写都想得不全面,以后要注意啊--求期望部分也不熟练,和平均数搞混也是orz,我已经是个期望都求不出来的废人了.这道题显然(大概)每个段的贡献是val[i]*(y-i+1)*(i-x+1);整体来说算是一看就是线段树的题. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring>

BZOJ 4262 线段树+期望

思路: 把询问离线下来,查询max和查询min相似,现在只考虑查询max 令sum[l,r,x]表示l到r内的数为左端点,x为右端点的区间询问的答案 那么询问就是sun[l1,r1,r2]-sum[l1,r1,l1-1] 从1到n枚举x,维护区间线段树表示sum[l,r,x],发现从x-1转移到x的过程中,每个数加上了max(a[pos]..a[x])的答案. 用单调队列维护一个单调递减的序列,由于a数列是随机的,这个队列期望有log个元素,所以只需要对这log段暴力修改,复杂度nlog^2n

Codeforces 895E Eyes Closed(线段树)

题目链接  Eyes Closed 题意  两个人玩一个游戏,现在有两种操作: 1.两个人格子挑选一个区间,保证两个的区间不相交.在这两个区间里面各选出一个数,交换这两个数. 2.挑选一个区间,求这个区间的和的期望. 对于第一种操作,先求出两个区间的长度len1和len2,再求出两个区间的期望和s1和s2. 对于第一个区间,我们先把这个区间里的所有数(期望值)乘上(len1 - 1)/(len1),再加上s2/len1/len2 对于第二个区间,我们先把这个区间里的所有数(期望值)乘上(len2

hdu-5805 NanoApe Loves Sequence(线段树+概率期望)

题目链接: NanoApe Loves Sequence Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 262144/131072 K (Java/Others) Problem Description NanoApe, the Retired Dog, has returned back to prepare for the National Higher Education Entrance Examination! In

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

P3924 康娜的线段树(期望)

P3924 康娜的线段树 看起来$O(nlogn)$可过其实由于巨大常数是无法通过的 $O(nlogn)$:70pts 我们手玩样例发现 线段树上某个节点的期望值$f[o]=(f[lc]+f[rc])/2+sum[o]$ $s[o]$表示该节点代表的区间和. 每次$Add(l,r,x)$时,每个x对于$f[o]$的贡献是固定的,即$f[o]+=x*k[o]$ 这个$k[o]$可以在建树时预处理. 然鹅卡不过TAT #include<iostream> #include<cstdio>

[JSOI2008][BZOJ1012] 最大数(动态开点线段树)

题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前数列的长度. 2. 插入操作. 语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾. 限制:n是整数(可能为负数)并且在长整范围内. 注意:初始时数列是空的,没有一个数. 输入输出格式 输入格式: 第一行两个整数,M

【BZOJ-4631】踩气球 线段树 + STL

4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 224  Solved: 114[Submit][Status][Discuss] Description 六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着Ai个气球. SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆. 这M个熊孩子每个人都指定了一个盒子区间[Li, R