(线段树+2个lazy标记)
题意:先给定
n(n<105)
个数字
ai(ai<109)
,再给出
q(q<105)
个操作,操作分两种类型,1.将这些数字里
[l1,r1]
区间中任意一个数字和
[l2,r2]
区间中任意一个数字交换。2.求
[l,r]
区间所有数字的和(数学期望值)
思路:数组上的区间修改和查询,想到用线段树,这里线段树的每个结点维护三个值:数学期望总和,加法标记和乘法标记。为什么呢?假如题目给出一个操作1,那么对于 [l1,r1] 区间中的某个数字x而言,在操作1后,它的数学期望变成了 x⋅r1−l1r1−l1+1+∑i=r2i=l2air2−l2+1⋅1r1−l1+1 (左项为该数字没有被交换的期望,右项为被交换的期望)。这里要注意操作1时, [l1,r1] 里的数学期望都是不一样的,因为乘法和除法针对的是这个区间里每一个特定的数字,而不是整个区间(所以必须维护加法标记和乘法标记)
代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
using namespace std;
const int maxn = 100050;
const double eps = 1e-6;
double a[maxn], sum[maxn<<2];
double addv[maxn<<2], multv[maxn<<2];
int dcmp(double x) {
if(fabs(x) < eps) return 0;
return (x < 0 ? -1:1);
}
void push_up(int p) {
sum[p] = sum[p<<1] + sum[p<<1|1];
}
/** lazy tags are designed for the lower nodes. **/
void push_down(int p, int l, int r) {
if(dcmp(multv[p])!=1 || dcmp(addv[p])!=0) {
multv[p<<1] *= multv[p];
multv[p<<1|1] *= multv[p];
addv[p<<1] = multv[p]*addv[p<<1] + addv[p];
addv[p<<1|1] = multv[p]*addv[p<<1|1] + addv[p];
int mid = (l+r) >> 1;
sum[p<<1] = multv[p]*sum[p<<1] + addv[p]*(mid-l+1);
sum[p<<1|1] = multv[p]*sum[p<<1|1] + addv[p]*(r-mid);
multv[p] = 1; addv[p] = 0;
}
}
void build(int p, int l, int r) {
multv[p] = 1; addv[p] = 0;
if(l == r) {
sum[p] = a[l];
return ;
}
int mid = (l+r) >> 1;
build(lson); build(rson);
push_up(p);
}
/** y = k*x + b; **/
void modify(int p, int l, int r, int L, int R, double k, double b) {
if(L <= l && r <= R) {
sum[p] = k*sum[p] + b*(r-l+1);
multv[p] *= k;
addv[p] = k*addv[p] + b;
return ;
}
push_down(p, l, r);
int mid = (l+r) >> 1;
if(L <= mid) modify(lson, L, R, k, b);
if(R > mid) modify(rson, L, R, k, b);
push_up(p);
}
double query(int p, int l, int r, int L, int R) {
if(L <= l && r <= R)
return sum[p];
push_down(p, l, r);
double ret = 0;
int mid = (l+r) >> 1;
if(L <= mid) ret += query(lson, L, R);
if(R > mid) ret += query(rson, L, R);
return ret;
}
int main() {
//freopen("test.txt","r",stdin);
int n, q;
scanf("%d%d",&n,&q);
for(int i=1; i<=n; i++)
scanf("%lf",&a[i]);
build(1, 1, n);
while(q --) {
int op, l1, r1, l2, r2;
scanf("%d",&op);
if(op == 1) {
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
double size1 = r1 - l1 + 1, size2 = r2 - l2 + 1;
double t1 = query(1, 1, n, l1, r1), t2 = query(1, 1, n, l2, r2);
//printf("query t1: %f t2:%f\n",t1,t2);
modify(1, 1, n, l1, r1, (size1-1)/size1, t2/(size2*size1));
modify(1, 1, n, l2, r2, (size2-1)/size2, t1/(size1*size2));
}
else {
scanf("%d%d",&l1,&l2);
double ans = query(1, 1, n, l1, l2);
printf("%f\n",ans);
}
}
return 0;
}