洛谷传送门
BZOJ传送门
题目描述
Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。
Y901高速公路是一条由 N−1 N − 1 段路以及 N N 个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为,从收费站 i i 行驶到(或从 i+1 i + 1 行驶到 i i )需要收取的费用。高速路刚建成时所有的路段都是免费的。
政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。
无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的 l,r(l<r) l , r ( l < r ) ,在第 l l 个到第个收费站里等概率随机取出两个不同的收费站 a a 和,那么从 a a 行驶到将期望花费多少费用呢?
输入输出格式
输入格式:
第一行 2 2 个正整数,表示有 N N 个收费站,次调整或询问
接下来 M M 行,每行将出现以下两种形式中的一种
C l r v
表示将第个收费站到第
r
r
个收费站之间的所有道路的通行费全部增加
Q l r
表示对于给定的
l,r
l
,
r
,要求回答小A的问题
所有C与Q操作中保证 1≤l<r≤N 1 ≤ l < r ≤ N
输出格式:
对于每次询问操作回答一行,输出一个既约分数
若答案为整数 a a ,输出
输入输出样例
输入样例#1:
4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4
输出样例#1:
1/1
8/3
17/6
说明
所有 C C 操作中的的绝对值不超过 10000 10000
在任何时刻任意道路的费用均为不超过 10000 10000 的非负整数
所有测试点的详细情况如下表所示
Test N M
1 =10 =10
2 =100 =100
3 =1000 =1000
4 =10000 =10000
5 =50000 =50000
6 =60000 =60000
7 =70000 =70000
8 =80000 =80000
9 =90000 =90000
10 =100000 =100000
解题分析
此题需要维护 [L,R] [ L , R ] 内所有子序列的和, 并支持区间加操作。很显然可以线段树维护这些信息。
设 Ansx A n s x 为当前区间的答案, lsumx l s u m x 为以 Lx L x 为起点的前缀的和, rsumx r s u m x 为以 Rx R x 为以 Rx R x 为终点的后缀的和, sumx s u m x 为区间权值和, lenx l e n x 为区间长度。
考虑如何合并两个区间。设 rs r s 为 x x 的右儿子, 为 x x 的左儿子。分开考虑左右区间的贡献, 可以得到以下合并公式:
- ;
- sumx=sumls+sumrs s u m x = s u m l s + s u m r s ;
- lsumx=lsumls+lsumrs+sumls×lenrs l s u m x = l s u m l s + l s u m r s + s u m l s × l e n r s
- rsumx=rsumls+rsumrs+sumrs×lenls r s u m x = r s u m l s + r s u m r s + s u m r s × l e n l s
- Ansx=Ansls+Ansrs+lsumrs×lenls+rsumls×lenrs A n s x = A n s l s + A n s r s + l s u m r s × l e n l s + r s u m l s × l e n r s
然后考虑区间加对答案的影响。 设区间变化值为 del d e l , 则有:
- sumx=sumx+del×lenx s u m x = s u m x + d e l × l e n x
- lsumx=lsumx+(lenx+1)×lenx2×del l s u m x = l s u m x + ( l e n x + 1 ) × l e n x 2 × d e l
- rsumx=rsumx+(lenx+1)×lenx2×del r s u m x = r s u m x + ( l e n x + 1 ) × l e n x 2 × d e l
这些都好说, 但 Ansx A n s x 的变化值需要考虑以下。 我们考虑区间内每种长度的序列出现的次数,计算所有元素总出现次数:
T=∑i=1lenx(lenx−i+1)×i T = ∑ i = 1 l e n x ( l e n x − i + 1 ) × i
两边加上 ∑lenxi=1i2 ∑ i = 1 l e n x i 2 就变成了
T+∑i=1lenxi2=∑i=1lenx(lenx+1)×i→T+lenx×(lenx+1)×(lenx×2+1)6=(lenx+1)×(lenx+1)×lenx2 T + ∑ i = 1 l e n x i 2 = ∑ i = 1 l e n x ( l e n x + 1 ) × i → T + l e n x × ( l e n x + 1 ) × ( l e n x × 2 + 1 ) 6 = ( l e n x + 1 ) × ( l e n x + 1 ) × l e n x 2
然后就可以 O(1) O ( 1 ) 算出 Ansx A n s x 的变化值了…查询时和 pushup p u s h u p 一样按上述方式合并区间。
代码如下:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <cctype> #include <algorithm> #define R register #define IN inline #define gc getchar() #define W while #define MX 100050 #define ll long long bool neg; template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc) if(c == '-') neg = true; for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; if(neg) neg = false, x = -x; } struct Node {ll lsum, rsum, sum, len, ans, tag;} tree[MX << 2]; int dot, q; namespace SGT { #define ls (now << 1) #define rs (now << 1 | 1) IN ll F1(ll x) {return x * (x + 1) * (x + 1) / 2;} IN ll F2(ll x) {return x * (x + 1) * (x * 2 + 1) / 6;} IN void pushdown(R int now) { if(tree[now].tag) { ll buf; tree[ls].tag += tree[now].tag; tree[rs].tag += tree[now].tag; buf = tree[ls].len * (tree[ls].len + 1) / 2 * tree[now].tag; tree[ls].lsum += buf, tree[ls].rsum += buf; buf = tree[rs].len * (tree[rs].len + 1) / 2 * tree[now].tag; tree[rs].lsum += buf, tree[rs].rsum += buf; tree[ls].sum += tree[now].tag * tree[ls].len; tree[rs].sum += tree[now].tag * tree[rs].len; tree[ls].ans += (F1(tree[ls].len) - F2(tree[ls].len)) * tree[now].tag; tree[rs].ans += (F1(tree[rs].len) - F2(tree[rs].len)) * tree[now].tag; tree[now].tag = 0; } } IN void pushup(R int now) { tree[now].sum = tree[ls].sum + tree[rs].sum; tree[now].lsum = tree[ls].lsum + tree[rs].lsum + tree[ls].sum * tree[rs].len; tree[now].rsum = tree[ls].rsum + tree[rs].rsum + tree[rs].sum * tree[ls].len; tree[now].ans = tree[ls].ans + tree[rs].ans + tree[ls].len * tree[rs].lsum + tree[rs].len * tree[ls].rsum; } IN Node merge (const Node &x, const Node &y) { Node ret; ret.len = x.len + y.len; ret.sum = x.sum + y.sum; ret.lsum = x.lsum + y.lsum + x.sum * y.len; ret.rsum = x.rsum + y.rsum + y.sum * x.len; ret.ans = x.ans + y.ans + x.len * y.lsum + y.len * x.rsum; return ret; } void build(R int now, R int lef, R int rig) { if(lef == rig) return tree[now].len = 1, void(); int mid = lef + rig >> 1; build(ls, lef, mid), build(rs, mid + 1, rig); tree[now].len = tree[ls].len + tree[rs].len; } void modify(R int now, R int lef, R int rig, R int lb, R int rb, R int del) { if(lef >= lb && rig <= rb) { ll buf; tree[now].tag += del; tree[now].sum += tree[now].len * del; buf = tree[now].len * (tree[now].len + 1) * del / 2; tree[now].lsum += buf, tree[now].rsum += buf; tree[now].ans += (F1(tree[now].len) - F2(tree[now].len)) * del; return; } pushdown(now); int mid = lef + rig >> 1; if(lb <= mid) modify(ls, lef, mid, lb, rb, del); if(rb > mid) modify(rs, mid + 1, rig, lb, rb, del); pushup(now); } Node query(R int now, R int lef, R int rig, R int lb, R int rb) { if(lef >= lb && rig <= rb) return tree[now]; int mid = lef + rig >> 1; pushdown(now); if(rb <= mid) return query(ls, lef, mid, lb, rb); else if(lb > mid) return query(rs, mid + 1, rig, lb, rb); else return merge(query(ls, lef, mid, lb, rb), query(rs, mid + 1, rig, lb, rb)); } #undef ls #undef rs } ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} int main(void) { char buf[3]; int a, b, c; ll as, g, tar; in(dot), in(q); Node res; SGT::build(1, 1, dot - 1); W (q--) { scanf("%s", buf); if(buf[0] == 'C') { in(a), in(b), in(c); SGT::modify(1, 1, dot - 1, a, b - 1, c); } else { in(a), in(b); res = SGT::query(1, 1, dot - 1, a, b - 1); as = res.ans, tar = 1ll * (b - a) * (b - a + 1) / 2; g = gcd(tar, as), printf("%lld/%lld\n", as / g, tar / g); } } }