Codeforces 895E Eyes Closed(线段树)

题目链接  Eyes Closed

题意  两个人玩一个游戏,现在有两种操作:

1、两个人格子挑选一个区间,保证两个的区间不相交。在这两个区间里面各选出一个数,交换这两个数。

2、挑选一个区间,求这个区间的和的期望。

对于第一种操作,先求出两个区间的长度len1和len2,再求出两个区间的期望和s1和s2。

对于第一个区间,我们先把这个区间里的所有数(期望值)乘上(len1 - 1)/(len1),再加上s2/len1/len2

对于第二个区间,我们先把这个区间里的所有数(期望值)乘上(len2 - 1)/(len2),再加上s1/len1/len2

线段树维护这两个操作即可。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define	ls		(i << 1)
#define	rs		(i << 1 | 1)
#define	mid		((L + R) >> 1)
#define	lson		i << 1, L, mid
#define	rson		i << 1 | 1, mid + 1, R

typedef long long LL;

const int N = 4e5 + 10;

double add[N], mul[N], s[N];
int n, q;

void pushup(int i){ s[i] = s[ls] + s[rs]; }

void pushdown(int i, int L, int R){
	s[ls] = mul[i] * s[ls] + add[i] * (mid - L + 1);
	mul[ls] *= mul[i];
	add[ls] = mul[i] * add[ls] + add[i];
       	s[rs] = mul[i] * s[rs] + add[i] * (R - mid);
	mul[rs] *= mul[i];
	add[rs] = mul[i] * add[rs] + add[i];
	add[i] = 0;
	mul[i] = 1.0;
}	

void build(int i, int L, int R){
	add[i] = 0, mul[i] = 1;
	s[i] = 0;
	if (L == R){ scanf("%lf", s + i); return; }
	build(lson);
	build(rson);
	pushup(i);
}

void add_update(int i, int L, int R, int l, int r, double val){
	if (l <= L && R <= r){
		s[i] += (R - L + 1) * val;
		add[i] += val;
		return;
	}

	pushdown(i, L, R);
	if (l <= mid) add_update(lson, l, r, val);
	if (r  > mid) add_update(rson, l, r, val);
	pushup(i);
}

void mul_update(int i, int L, int R, int l, int r, double val){
	if (l <= L && R <= r){
		s[i] *= val;
		mul[i] *= val;
		add[i] *= val;
		return;
	}

	pushdown(i, L, R);
	if (l <= mid) mul_update(lson, l, r, val);
	if (r  > mid) mul_update(rson, l, r, val);
	pushup(i);
}

double query(int i, int L, int R, int l, int r){
	if (l <= L && R <= r) return s[i];
	double ret = 0;
	pushdown(i, L, R);
       	if (l <= mid) ret += query(lson, l, r);
	if (r  > mid) ret += query(rson, l, r);
	pushup(i);
	return ret;
}	

int main(){

	scanf("%d%d", &n, &q);
	build(1, 1, n);

	while (q--){
		int op;
		scanf("%d", &op);
		if (op == 1){
			int l1, r1, l2, r2;
			scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
			double s1 = query(1, 1, n, l1, r1);
			double s2 = query(1, 1, n, l2, r2);
			double len1 = r1 - l1 + 1;
			double len2 = r2 - l2 + 1;
			mul_update(1, 1, n, l1, r1, 1.0 * (len1 - 1) / len1);
			mul_update(1, 1, n, l2, r2, 1.0 * (len2 - 1) / len2);
			add_update(1, 1, n, l1, r1, 1.0 / len1 / len2 * s2);
			add_update(1, 1, n, l2, r2, 1.0 / len1 / len2 * s1);
		}

		else{
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%.12f\n", query(1, 1, n, l, r));
		}
	}

	return 0;
}
时间: 2024-08-30 10:59:21

Codeforces 895E Eyes Closed(线段树)的相关文章

[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心) 题面 给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个.然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小.给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\). 分析 显然最后得到的序列是小的数在一边,大的数在另一边.设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么

Codeforces 46D Parking Lot(线段树)

题目链接:Codeforces 46D Parking Lot 题目大意:一个街道,长为N,每辆车停进来的时候必须和前面间隔B米,和后面间隔F米,现在用两种操作,1是停进来一个长为x的车,2是第x辆车开走. 解题思路:区间合并,建一颗长度为N + B + F的线段树,然后每次停车进去的时候都查询x + B + F的区间,然后修改的时候只修改x的长度. #include <cstdio> #include <cstring> #include <algorithm> us

Codeforces 482B Interesting Array(线段树)

题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,现在有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 否有满足的数列. 解题思路:线段树维护,每条限制等于是对l~r之间的数或上q(取且的性质,相应二进制位一定为1),那么处理完所有的 限制,在进行查询,查询对应每个l~r之间的数取且是否还等于q.所以用线段树维护取且和,修改为或操作. #include <cstdio> #include <c

Codeforces 755D(思维+线段树)

http://codeforces.com/problemset/problem/755/D 从X到X+k点,其实只要求从X到X+k之间的点有多少条线超过X--X+K这条线就行,一开始直接暴力,就时间超时了,而用线段树维护就快多了. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 #define N 1000010 5 #define INF 0x3f3f3f3f 6 #define lso

Codeforces 777D Hanoi Factory(线段树维护DP)

题目链接 Hanoi Factory 很容易想到这是一个DAG模型,那么状态转移方程就出来了. 但是排序的时候有个小细节:b相同时看a的值. 因为按照惯例,堆塔的时候肯定是内半径大的在下面. 因为N有1e5,那么DP的时候用线段树优化一下,就可以了. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for(int i(a); i <= (b); ++i) 6 7 typedef lo

CodeForces 91B Queue (线段树单点操作)

Description There are n walruses standing in a queue in an airport. They are numbered starting from the queue's tail: the 1-st walrus stands at the end of the queue and the n-th walrus stands at the beginning of the queue. The i-th walrus has the age

codeforces 522D. Closest Equals 线段树+离线

题目链接 n个数m个询问, 每次询问输出给定区间中任意两个相同的数的最近距离. 先将询问读进来, 然后按r从小到大排序, 将n个数按顺序插入, 并用map统计之前是否出现过, 如果出现过, 就更新线段树. 如果当前的i等于某个询问的r, 那么就查询, 具体看代码. 1 #include <iostream> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <

CodeForces 19D Points(线段树+map)

开始想不通,后来看网上说是set,就有一个想法是对每个x建一个set...然后又想直接建立两重的set就好,最后发现不行,自己想多了...  题意是给你三种操作:add (x y) 平面添加(x y)这个点 remove (x y)平面删除(x y)这个点 find (x y) 查找(x y)这个点严格的右上方中最左边的点,有多个就再找最下方的点,输出 其实想通了还是比较简单的,我的想法就是对于x先排序再对y排序,这样建一颗线段树,用处在于:添加和删除都可以当成单点更新,只需要记录最大值就好.f

Codeforces 460C 二分结果+线段树维护

发现最近碰到好多次二分结果的题目,上次多校也是,被我很机智的快速过了,这个思想确实非常不错.在正面求比较难处理的时候,二分结果再判断是否有效往往柳暗花明. 这个题目给定n个数字的序列,可以操作m次,每次要操作w个连续的数字,每次的操作将使得该段连续数字的数都+1,最后求整个序列最小值的最大值 求最小值最大,明显的二分结果的题目,我一开始还是在ACdream那个群里看到这个题,说是二分+线段树的题目,我就来做了一下..首先二分部分很容易,下界就是初始序列的最小值,上界就是 下界+m,至于怎么判断这