Bzoj 2752: [HAOI2012]高速公路(road)

传送门: http://www.lydsy.com/JudgeOnline/problem.php?id=2752
题面:
  Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。
Y901高速公路是一条由 N1 段路以及 N 个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为 1 ~ N ,从收费站 i 行驶到 i+1 (或从 i+1 行驶到 i )需要收取 Vi 的费用。高速路刚建成时所有的路段都是免费的。
  政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。
  无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的 l,r (l<r) ,在第 l 个到第 r 个收费站里等概率随机取出两个不同的收费站 a b ,那么从 a 行驶到 b 将期望花费多少费用呢?
数据范围: n,m105
题解: 这道题的修改直接线段树即可,关键是询问,询问的是一段区间内的所有连续子区间的和,对于这个我们在每个节点顺便维护当前区间的大小,所有前缀的和,所有后缀的和和所有连续子区间的和,这样合并的时候就可以用用前三者算出当前所有连续子区间的和。

#include<bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 10;
const int Node = N * 4;
template <typename T> void read(T &x) {
    x = 0; T f = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f *= -1;
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    x *= f;
}
struct rec{
    int size;
    ll sum, suml, sumr, suma;
}tr[Node];
rec operator + (const rec &l, const rec &r) {
    return (rec) {l.size + r.size, l.sum + r.sum, l.suml + r.suml + l.sum * r.size, l.sumr + r.sumr + r.sum * l.size, 
                    l.suma + r.suma + l.sumr * r.size + r.suml * l.size};
}
int n, m, l, r, c, lc[Node], rc[Node], cnt;
ll tri[N], tp[N], tag[Node], sum[Node]; //triangular_pyramidal
char op[20];
void build(int i, int l, int r) {
    tr[i].size = r - l + 1;
    if (l == r) return;
    build(lc[i] = ++cnt, l, (l + r) / 2);
    build(rc[i] = ++cnt, (l + r) / 2 + 1, r);
}
void chg(int i, int l, int r, int L, int R, ll c);
void push(int i, int l, int r) {
    if (!tag[i]) return;
    chg(lc[i], l, (l + r) / 2, l, (l + r) / 2, tag[i]);
    chg(rc[i], (l + r) / 2 + 1, r, (l + r) / 2 + 1, r, tag[i]);
    tag[i] = 0;
}
void chg(int i, int l, int r, int L, int R, ll c) {
    if (l > R || r < L) return;
    if (l >= L && r <= R) {
        tag[i] += c;
        tr[i].sum  += c * tr[i].size;
        tr[i].suml += c * tri[tr[i].size];
        tr[i].sumr += c * tri[tr[i].size];
        tr[i].suma += c * tp[tr[i].size];
        return;
    }
    push(i, l, r);
    chg(lc[i], l, (l + r) / 2, L, R, c);
    chg(rc[i], (l + r) / 2 + 1, r, L, R, c);
    tr[i] = tr[lc[i]] + tr[rc[i]];
}
rec qry(int i, int l, int r, int L, int R) {
    if (l == L && r == R) return tr[i];
    push(i, l, r);
    int mid = (l + r) / 2;
    if (R <= mid) return qry(lc[i], l, mid, L, R);
    if (L >  mid) return qry(rc[i], mid + 1, r, L, R);
    return qry(lc[i], l, mid, L, mid) + qry(rc[i], mid + 1, r, mid + 1, R);
}
ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);}
int main() {
    read(n); read(m); n--;
    cnt = 1; build(1, 1, n);
    for (int i = 1; i <= n; i++)
        tri[i] = (ll)i * (i + 1) / 2,
        tp[i] = tp[i - 1] + tri[i];
    while (m--) {
        scanf("%s", op); read(l); read(r); r--;
        if (op[0] == 'C')
            read(c), chg(1, 1, n, l, r, c);
        else {
            ll ans = qry(1, 1, n, l, r).suma, div = (ll)(r - l + 2) * (r - l + 1) / 2, g = gcd(ans, div);
            printf("%lld/%lld\n", ans / g, div / g);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值