【题目描述】
24OI的成员跑到了一片大森林,目的是摘一些毒瘤回去给高一同学吃。
于是在毒瘤树下,cyl沿着一条线钉了N个木桩,并且在相邻两个木桩之间结网,初始时所有的网中都没有毒瘤。
之后,树上可能会掉下毒瘤,cyl也可能拿走一部分毒瘤,我们统一把它们称为“操作”。
在等毒瘤落下的cyl同学正在思考一个问题:对于给定的l,r(l<r),在第l个木桩到第r个木桩里等概率随机取出两个不同的木桩a和b,那么从min(a,b)到max(a,b)之间的所有的网中的毒瘤总数期望有多少呢?
【输入输出格式】
输入格式:
第一行2个正整数N,M,表示有N个木桩,M个操作或询问。
接下来M行,每行将出现以下两种形式中的一种:
C l r v
如果v>=0,表示第l个木桩到第r个木桩之间的每个网中都接到了v个毒瘤;
否则表示cyl从第l个木桩到第r个木桩之间的每个网中都拿走了v个毒瘤;
Q l r
表示对于给定的l,r,要求回答cyl的问题;
所有操作或询问中保证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
8/3
17/6
【说明】
所有C操作中的v的绝对值不超过10000。
在任何时刻任意网内的毒瘤个数均为不超过10000的非负整数。
所有测试点的详细情况如下表所示:
Test N M
1 =10 =10
2 =20 =20
3 =100 =100
4 =300 =300
5 =350 =350
6 =2000 =2000
7 =5000 =5000
8 =10000 =10000
9 =50000 =50000
10 =100000 =100000
首先得知道期望是什么:就是该区间内 a, b 的每一种情况所对应的毒瘤数之和除以情况数目。
对了,题中说是两个木桩之间一个网兜,这么做不太方便,我们默认第i个网兜就属于这个网兜左边连着的木桩。
所以对于所有的r,都应r--。
接着说期望,比如区间2到5,那么总情况数就是 [2, 2], [2, 3], [2, 4], [2, 5], [3, 3], [3, 4], [3, 5], [4, 4], [4, 5], [5, 5]。
那么对于长度为 L (L = r - l + 1)的区间,总情况数就是1 + 2 + 3 + 4 + …… + L。
因此只要预处理一个前缀和数组就能解决分母了。
接下来是分子,即每一个情况所对应的毒瘤数之和。
我们可以换一种思路,去求网兜i在 l 到r区间里总共出现了多少次。
于是易得
次数 = (i - l + 1) * (r - i + 1) * w[i] (1)
w[i]为当前网兜 i 里的毒瘤数
于是
总次数 = sum((i - l +1) * (r - i + 1) * w[i]) (i : l -> r)
对于(1), 当我们把括号打开后
次数 = - i * i * w[i] + (r + l) * i * w[i] + (-l * r - l + r + 1) * w[i]
于是线段树就派上用场了,开一个线段树,同时维护sum1(w[i]), sum2(i * w[i]), sum3(i * i * w[i]).
对于sum1(w[i]),就是普通的区间求和。
对于sum2(i * w[i]),若是区间每一个数加 v,则这个区间的sum2就增加了(l + (l + 1) + (l + 2) + …… + r)。于是我们可以预处理一个前缀和(其实和分母的前缀和一样)。
对于sum3(i * i * w[i]),若是区间每一个数加v,则这个区间sum3就增加了(l ^ 2 + (l + 1) ^ 2 + (l + 2) ^ 2 +…… + r ^ 2)。于是同样可以预处理一个i ^ 2的前缀和。
综上,一个lazy[],三个sum[].
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const int maxn = 2e5 + 5; 9 10 int n, m; 11 ll sigma1[maxn], sigma2[maxn]; 12 ll ans1 = 0, ans2 = 0, ans3 = 0; 13 14 void init(int n) 15 { 16 for(ll i = 1; i <= n; ++i) 17 { 18 sigma1[i] = sigma1[i - 1] + i; 19 sigma2[i] = sigma2[i - 1] + i * i; 20 } 21 } 22 23 struct Tree 24 { 25 int l, r; 26 ll sum1, sum2, sum3; //w[i], i * w[i],i * i * w[i] 27 int lazy; 28 }tree[maxn << 2]; 29 void build(int L, int R, int now) 30 { 31 tree[now].l = L; tree[now].r = R; 32 if(L == R) return; 33 int mid = (L + R) >> 1; 34 build(L, mid, now << 1); 35 build(mid + 1, R, now << 1 | 1); 36 } 37 void update(int L, int R, int now, int v) 38 { 39 if(tree[now].l == L && tree[now].r == R) 40 { 41 tree[now].lazy += v; 42 return; 43 } 44 tree[now].sum1 += (ll) v * (R - L + 1); 45 tree[now].sum2 += (ll) v * (sigma1[R] - sigma1[L - 1]); 46 tree[now].sum3 += (ll) v * (sigma2[R] - sigma2[L - 1]); 47 int mid = (tree[now].l + tree[now].r) >> 1; 48 if(R <= mid) update(L, R, now << 1, v); 49 else if(L > mid) update(L, R, now << 1 | 1, v); 50 else {update(L, mid, now << 1, v); update(mid + 1, R, now << 1 | 1, v);} 51 } 52 void pushdown(int now) 53 { 54 if(tree[now].lazy) 55 { 56 tree[now].sum1 += (ll) tree[now].lazy * (tree[now].r - tree[now].l + 1); 57 tree[now].sum2 += (ll) tree[now].lazy * (sigma1[tree[now].r] - sigma1[tree[now].l - 1]); 58 tree[now].sum3 += (ll) tree[now].lazy * (sigma2[tree[now].r] - sigma2[tree[now].l - 1]); 59 tree[now << 1].lazy += tree[now].lazy; 60 tree[now << 1 | 1].lazy += tree[now].lazy; 61 tree[now].lazy = 0; 62 } 63 } 64 void query(int L, int R, int now) 65 { 66 pushdown(now); 67 if(tree[now].l == L && tree[now].r == R) 68 { 69 ans1 += tree[now].sum1; 70 ans2 += tree[now].sum2; 71 ans3 += tree[now].sum3; 72 return; 73 } 74 int mid = (tree[now].l + tree[now].r) >> 1; 75 if(R <= mid) query(L, R, now << 1); 76 else if(L > mid) query(L, R, now << 1 | 1); 77 else {query(L, mid, now << 1); query(mid + 1, R, now << 1 | 1);} 78 } 79 ll gcd(ll x,ll y) 80 { 81 return y ? gcd(y, x % y) : x; 82 } 83 84 int main() 85 { 86 scanf("%d%d", &n, &m); 87 init(n); 88 build(1, n, 1); 89 for(int i = 1; i <= m; ++i) 90 { 91 char c; int L, R; 92 cin >> c; scanf("%d%d", &L, &R); 93 R--; 94 if(c == 'C') 95 { 96 int v; scanf("%d", &v); 97 update(L, R, 1, v); 98 } 99 else 100 { 101 ans1 = ans2 = ans3 = 0; 102 query(L, R, 1); 103 ll Upans = (R - L - (ll)L * R + 1) * ans1 + (R + L) * ans2 - ans3; 104 ll Downans = sigma1[R - L + 1]; 105 ll div = gcd(Upans, Downans); 106 printf("%lld/%lld\n", Upans / div, Downans / div); 107 } 108 } 109 return 0; 110 }