zoukankan      html  css  js  c++  java
  • [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) = sumfrac{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
    ", SGMTtree :: query(1, 1, n, l, r));
    		}
    	}
    }
    
    int main() {
    //	freopen("CF895E.in", "r", stdin);
    //	freopen("CF895E.out", "w", stdout);
    
    	init();
    	solve();
    
    #ifdef Qrsikno
        debug("
    Running time: %.3lf(s)
    ", clock() * 1.0 / CLOCKS_PER_SEC);
    #endif
        return 0;
    }
    

    调试:

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

  • 相关阅读:
    第二次冲刺 站立会议7
    第二次冲刺 站立会议6
    第二次冲刺 站立会议5
    第二次冲刺 站立会议4
    第二次冲刺 站立会议3
    第二次冲刺 站立会议2
    第二次冲刺 站立会议1
    第一次冲刺 站立会议9
    第一次冲刺 站立会议8
    第一次冲刺 站立会议7
  • 原文地址:https://www.cnblogs.com/qrsikno/p/11511080.html
Copyright © 2011-2022 走看看