题目大意:
题目链接
给出一个序列
,初始值为0。
有一个问题:
给定
,在第
个到第
个数之间等概率随机的挑选两个不同的数
, 那么
之间的区间和的期望值是多少。
有以下两种操作:
C l r v 表示区间从
到
的值增加
。
Q l r 回答上述问题。
思路:
题目中的 表示节点,在这里我们直接用 表示节点之间的路,那么我们进行操作之间要把 ,才能让节点对应成节点之间的路,具体细节参看主函数。
容易想到暴力算期望的方法,每个区间被取到的概率为:
。
那么期望就是 :
。
显然这个式子不太容易计算。
我们考虑每个区间对期望的贡献:
。
我们发现只要
这个元素处于一个区间中,它就会对期望贡献
。
想到这儿,我们就可以从每一个元素来考虑这个问题,我们只需要计算出多少个区间包含
, 然后包含
的区间个数乘
,即是
对期望的贡献值。我们一次考虑区间内的每一个元素即可。
根据组合数学的知识,我们可以知道,包含
的区间个数为
。
那么我们要求的最后的答案就是:
。
那么接下来的问题就是上面式子的左半部分怎么求的问题:
我们将累加符号中的式子展开变成:
。
我们只需要在线段树中维护三个量:
的区间和,
的区间和,
的区间和即可通过线性的组合得到上面的式子。
还需要注意的一点是区间修改的操作,对于维护的第一个值来说就是普通的修改。
而对于第二个值来说,区间增加的值是
,乘的是一个等差数列,也就是
。
对于第三个值来说,区间增加的值是
,对于右半部分我们预处理出
的前缀和,那么上式就变成
。
线段树部分完成,具体的细节可以看代码。
代码:
/**
* Author : Xiuchen
* Date : 2020-02-29-18.40.16
* Description : 高速公路
*/
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 1e5 + 100;
ll gcd(ll a, ll b){
return b ? gcd(b, a % b) : a;
}
struct SegmentTree{
ll sum[5];
ll add;
int l, r;
#define l(x) t[x].l
#define r(x) t[x].r
#define add(x) t[x].add
#define sum1(x) t[x].sum[1]
#define sum2(x) t[x].sum[2]
#define sum3(x) t[x].sum[3]
}t[maxn * 4];
int n, m;
ll pre[maxn];
struct node{
ll sum1, sum2, sum3;
};
void push_up(int p){
sum1(p) = sum1(p * 2) + sum1(p * 2 + 1);
sum2(p) = sum2(p * 2) + sum2(p * 2 + 1);
sum3(p) = sum3(p * 2) + sum3(p * 2 + 1);
}
void push_down(int p){
if(add(p)){
sum1(p * 2) += add(p) * (r(p * 2) - l(p * 2) + 1);
sum1(p * 2 + 1) += add(p) * (r(p * 2 + 1) - l(p * 2 + 1) + 1);
sum2(p * 2) += add(p) * (l(p * 2) + r(p * 2)) * (r(p * 2) - l(p * 2) + 1) / 2;
sum2(p * 2 + 1) += add(p) * (l(p * 2 + 1) + r(p * 2 + 1)) * (r(p * 2 + 1) - l(p * 2 + 1) + 1) / 2;
sum3(p * 2) += add(p) * (pre[r(p * 2)] - pre[l(p * 2) - 1]);
sum3(p * 2 + 1) += add(p) * (pre[r(p * 2 + 1)] - pre[l(p * 2 + 1) - 1]);
add(p * 2) += add(p), add(p * 2 + 1) += add(p);
add(p) = 0;
}
}
void build(int p, int l, int r){
l(p) = l, r(p) = r;
if(l == r){
sum1(p) = sum2(p) = sum3(p) = add(p) = 0;
return;
}
int mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
push_up(p);
}
void change(int p, int l, int r, ll d){
if(l <= l(p) && r(p) <= r){
add(p) += d;
sum1(p) += d * (r(p) - l(p) + 1);
sum2(p) += d * (l(p) + r(p)) * (r(p) - l(p) + 1) / 2;
sum3(p) += d * (pre[r(p)] - pre[l(p) - 1]);
return;
}
int mid = (l(p) + r(p)) >> 1;
push_down(p);
if(l <= mid) change(p * 2, l, r, d);
if(mid < r) change(p * 2 + 1, l, r, d);
push_up(p);
}
node query(int p, int l, int r){
if(l <= l(p) && r(p) <= r){
node ans = {sum1(p), sum2(p), sum3(p)};
return ans;
}
int mid = (l(p) + r(p)) >> 1;
push_down(p);
if(l > mid) return query(p * 2 + 1, l, r);
else if(r <= mid) return query(p * 2, l, r);
else{
node ans1 = query(p * 2, l, r);
node ans2 = query(p * 2 + 1, l, r);
node ans;
ans.sum1 = ans1.sum1 + ans2.sum1, ans.sum2 = ans1.sum2 + ans2.sum2, ans.sum3 = ans1.sum3 + ans2.sum3;
return ans;
}
}
int main(){
scanf("%d%d", &n, &m);
build(1, 1, n);
for(int i = 1; i <= n; i++) pre[i] = pre[i - 1] + 1LL * i * i;
//预处理平方和,用与维护第三个值。
while(m--){
char op[5];
ll l, r;
scanf("%s%lld%lld", op, &l, &r);
if(op[0] == 'C'){
ll v;
scanf("%lld", &v);
change(1, l, r - 1, v);
//更新第l个点到第r个点之间的路,也就是更新第l段路到第r-1段路。
}
else{
r--;//r--,对应到路。
node ans = query(1, l, r);
ll tmp1 = (l + r) * ans.sum2 + ans.sum1 * (r - l - l * r + 1) - ans.sum3;
ll tmp2 = (r - l + 2) * (r - l + 1) / 2;
//这里是C(r - l + 2, 2),因为是在点之间取两个不同的点。
//点的总数是r + 1 - l + 1.
ll G = gcd(tmp1, tmp2);
printf("%lld/%lld\n", tmp1 / G, tmp2 / G);
}
}
return 0;
}