[HAOI2012]高速公路
题目大意
给你一个序列,询问一个区间求从中任选一个子区间的权值和的期望。操作带修。
题目分析
非常容易想到线段树。
套路一:注意到边权不容易考虑,我们转边权为点权。
对于询问 \([l,r]\),我们的期望值为 \(\sum\limits_{i=l}^r\sum\limits_{j=i}^r\dfrac{dist(i,j)}{{r-l+1}\choose 2}\)。下面那个可以轻易求出,我们重点看 \(\sum\limits_{i=l}^r\sum\limits_{j=i}^rdist(i,j)\)。
这个式子直接求是 \(\mathcal{O(n^3)}\) 的,根据数据范围我们需要把它优化到 \(\mathcal{O(\log n)}\)。我们看看有什么地方可以优化。
首先就是 \(dist(i,j)\),可以用前缀和优化,\(dist(i,j)\) 和 \(sum[j]-sum[i-1]\) 是等价的,其中 \(sum[i]=\sum\limits_{j=1}^iv_j\)。预处理出 \(sum\),单次询问 \(\mathcal{O(n^2)}\)。
还是不够。
套路二:直接算不容易,我们不妨计算每个值的贡献。
换而言之,套两个 \(\sum\) 不好求那么我们不如直接算每个 \(v_i\) 会被算几次。会被算几次呢?对于 \(v_i\),区间总数是 \((r-i+1)\times (i-l+1)\)。每次都会被算一次,所以 \(v_i\) 会被算 \((r-i+1)\times(i-l+1)\) 次。
式子转化为 \(\sum\limits_{i=l}^rv_i\times(r-i+1)\times(i-l+1)\),单次复杂度 \(\mathcal{O(n)}\)。
接下来推推式子:
红色部分抵消掉:
三个部分都可以用线段树来算。
代码
线段树中,我们用 \(s4,s5\) 分别存储区间和和区间平方和,即 \(\sum\limits_{i=l}^ri\) 和 \(\sum\limits_{i=l}^ri^2\)。用 \(s1,s2,s3\) 分别存储 \(\sum\limits_{i=l}^rv_i\),\(\sum\limits_{i=l}^rv_i\times i\) 和 \(\sum\limits_{i=l}^rv_i\times i^2\)。
//2022/5/10
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#define int long long
#define enter putchar(10)
#define debug(c,que) std::cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) std::cout << arr[i] << w;
#define speed_up() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define Abs(x) ((x) > 0 ? (x) : (-x))
#define stop return(0)
const int mod = 1e9 + 7;
inline int MOD(int x) {
if (x < 0) x += mod;
return x % mod;
}
namespace Newstd {
char buf[1 << 21],*p1 = buf,*p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
}
inline int read() {
int ret = 0,f = 0;char ch = getc();
while (!isdigit(ch)) {
if(ch == '-') f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = (ret << 3) + (ret << 1) + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
inline void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace Newstd;
const int N = 2e5 + 5;
int C[N][3];
int n,m,sum1,sum2,sum3;
inline int gcd(int a,int b) {
return b == 0 ? a : gcd(b,a % b);
}
inline void init() {
C[0][0] = 1;
for (register int i = 1;i <= 1e5; ++ i) {
C[i][0] = 1;
for (register int j = 1;j <= 2; ++ j) {
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
}
struct Segment_Tree {
struct Node {
int l,r;
int tag,s1,s2;
int s3,s4,s5;
} node[N << 2];
#define lson (p << 1)
#define rson (p << 1 | 1)
inline void pushup(int p) {
node[p].s1 = node[lson].s1 + node[rson].s1;
node[p].s2 = node[lson].s2 + node[rson].s2;
node[p].s3 = node[lson].s3 + node[rson].s3;
node[p].s4 = node[lson].s4 + node[rson].s4;
node[p].s5 = node[lson].s5 + node[rson].s5;
}
inline void build(int p,int l,int r) {
node[p].l = l,node[p].r = r;
if (l == r) {
node[p].s4 = l,node[p].s5 = l * l;
return;
}
int mid = l + r >> 1;
build(lson,l,mid),build(rson,mid + 1,r);
pushup(p);
}
inline void pushdown(int p) {
if (node[p].tag) {
node[lson].tag += node[p].tag,node[rson].tag += node[p].tag;
node[lson].s1 += node[p].tag * (node[lson].r - node[lson].l + 1);
node[rson].s1 += node[p].tag * (node[rson].r - node[rson].l + 1);
node[lson].s2 += node[p].tag * node[lson].s4;
node[rson].s2 += node[p].tag * node[rson].s4;
node[lson].s3 += node[p].tag * node[lson].s5;
node[rson].s3 += node[p].tag * node[rson].s5;
node[p].tag = 0;
}
}
inline void update(int x,int y,int p,int k) {
pushdown(p);
if (x <= node[p].l && node[p].r <= y) {
node[p].tag += k;
node[p].s1 += k * (node[p].r - node[p].l + 1);
node[p].s2 += k * node[p].s4,node[p].s3 += k * node[p].s5;
return;
}
int mid = node[p].l + node[p].r >> 1;
if (x <= mid) update(x,y,lson,k);
if (y > mid) update(x,y,rson,k);
pushup(p);
}
inline void query(int x,int y,int p) {
pushdown(p);
if (x <= node[p].l && node[p].r <= y) {
sum1 += node[p].s1,sum2 += node[p].s2,sum3 += node[p].s3;
return;
}
int mid = node[p].l + node[p].r >> 1;
if (x <= mid) query(x,y,lson);
if (y > mid) query(x,y,rson);
}
#undef lson
#undef rson
} seg;
#undef int
int main(void) {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
#define int long long
std::cin.tie(0),std::cout.tie(0);
std::cin >> n >> m;
init();
seg.build(1,1,n);
while (m --) {
char ch[2];
int l,r;
std::cin >> ch >> l >> r;
if (ch[0] == 'C') {
int k;
std::cin >> k;
seg.update(l + 1,r,1,k);
} else {
sum1 = sum2 = sum3 = 0;
seg.query(l + 1,r,1);
int up = (l + 1 + r) * sum2 + ((r - (l + 1)) + 1 - (l + 1) * r) * sum1 - sum3;
int down = C[r - l + 1][2];
int t = gcd(up,down);
std::cout << up / t << "/" << down / t << '\n';
}
}
return 0;
}