【题解】HAOI2012高速公路
一节政治课的结果……推式子+推式子+推式子……
首先注意到一个区间里面,选择(x, y)和(y, x)的费用是一样的。所以我们把这两种情况合为一种,那么现在询问的区间为(l, r),则一共的情况就有 1 / (k + 1)*(k)种 (k = r - l + 1)。所以我们只需要求出区间内所有的子集之和 * 2 / (k + 1) * k(每种情况有两种)。但这样复杂度还是太高了,我们考虑继续推下式子。
顺着一个比较常见的思路想:分离出每一段路对于答案的贡献再累加起来。那么我们的ans = Vx(这一段路的代价) * 包含了这条道路的区间个数。包含了第x条道路的区间个数一共是(x - l + 1) * (r - x)。但这个东西我们不好维护,所以将它拆分一下,尽量分离出不变的量。这个东西就等于:((rx + lx) - (x * x + x) + (r - l * r))* Vx。由此, 问题转化为维护区间内的 Vx 之和, Vx * x之和, 与 Vx * x * (x + 1)之和。线段树完美解决!
// luogu-judger-enable-o2 #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define int unsigned long long int n, m, mark[maxn * 3]; struct tree { int l, r, num[4], size, x, xx; }T[maxn * 40]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Build(int p, int l, int r) { T[p].l = l, T[p].r = r, T[p].size = (r - l + 1); if(l == r) { T[p].num[1] = T[p].num[2] = T[p].num[3] = 0; T[p].x = l, T[p].xx = T[p].x * (T[p].x + 1); return; } int mid = (l + r) >> 1; Build(p << 1, l, mid), Build(p << 1 | 1, mid + 1, r); T[p].x = T[p << 1].x + T[p << 1 | 1].x; T[p].xx = T[p << 1].xx + T[p << 1 | 1].xx; return; } void push_up(int p, int num) { T[p].num[1] += num * T[p].x; T[p].num[2] += num * T[p].xx; T[p].num[3] += num * T[p].size; mark[p] += num; } void push_down(int p) { if(!mark[p]) return; push_up(p << 1, mark[p]); push_up(p << 1 | 1, mark[p]); mark[p] = 0; } void update(int p, int l, int r, int num) // num1 :Vx * x, num2 :Vx * x * (x + 1), num3 : Vx; { int mid = (l + r) >> 1; int L = T[p].l, R = T[p].r; if(L >= l && R <= r) { push_up(p, num); return; } if(R < l || L > r) return; push_down(p); update(p << 1, l, r, num), update(p << 1 | 1, l, r, num); T[p].num[1] = T[p << 1].num[1] + T[p << 1 | 1].num[1]; T[p].num[2] = T[p << 1].num[2] + T[p << 1 | 1].num[2]; T[p].num[3] = T[p << 1].num[3] + T[p << 1 | 1].num[3]; } int query(int p, int l, int r, int opt) { int L = T[p].l, R = T[p].r; if(R < l || L > r) return 0; if(L >= l && R <= r) return T[p].num[opt]; push_down(p); return query(p << 1, l, r, opt) + query(p << 1 | 1, l, r, opt); } int Get(int a, int b) { while(b) { int c = a % b; a = b; b = c; } return a; } signed main() { n = read(), m = read(); Build(1, 1, n); for(int i = 1; i <= m; i ++) { char c; cin >> c; int l = read(), r = read(); if(c == 'C') { int v = read(); update(1, l, r - 1, v); } else // num1 :Vx * x, num2 :Vx * x * (x + 1), num3 : Vx; { int ans = query(1, l, r - 1, 1) * (l + r); ans -= query(1, l, r - 1, 2); ans += query(1, l, r - 1, 3) * (r - l * r); int K = (r - l + 1) * (r - l); int GCD = Get(ans * 2, K); printf("%lld/%lld\n", ans * 2 / GCD, K / GCD); } } return 0; }