[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)}\)

接下来推推式子:

\[\sum\limits_{i=l}^rv_i\times(r-i+1)\times(i-l+1) \]

\[=\sum\limits_{i=l}^rv_i\times(r\cdot i-l\cdot r+r-i^2+l\cdot i\color{red}{-i+i}\color{black}-l+1) \]

红色部分抵消掉:

\[=\sum\limits_{i=l}^rv_i\times(r\cdot i-l\cdot r+r-i^2+l\cdot i-l+1) \]

\[=\sum\limits_{i=l}^rv_i\times i\times(l+r)+\sum\limits_{i=l}^rv_i\times(r-l+1-l\cdot r)-\sum\limits_{i=l}^rv_i\times i^2 \]

三个部分都可以用线段树来算。

代码

线段树中,我们用 \(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;
}
posted @ 2022-05-10 21:44  Coros_Trusds  阅读(23)  评论(0编辑  收藏  举报