[Luogu P2221] [BZOJ 2752] [HAOI2012]高速公路

洛谷传送门
BZOJ传送门

题目描述

Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。

Y901高速公路是一条由 N1 N − 1 段路以及 N N 个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1N,从收费站 i i 行驶到i+1(或从 i+1 i + 1 行驶到 i i )需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。

政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。

无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的 l,r(l<r) l , r ( l < r ) ,在第 l l 个到第r个收费站里等概率随机取出两个不同的收费站 a a b,那么从 a a 行驶到b将期望花费多少费用呢?

输入输出格式

输入格式:

第一行 2 2 个正整数N,M,表示有 N N 个收费站,M次调整或询问

接下来 M M 行,每行将出现以下两种形式中的一种

C l r v 表示将第l个收费站到第 r r 个收费站之间的所有道路的通行费全部增加v

Q l r 表示对于给定的 l,r l , r ,要求回答小A的问题

所有C与Q操作中保证 1l<rN 1 ≤ l < r ≤ N

输出格式:

对于每次询问操作回答一行,输出一个既约分数

若答案为整数 a a ,输出a/1

输入输出样例

输入样例#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 操作中的v的绝对值不超过 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 的右儿子, ls x x 的左儿子。分开考虑左右区间的贡献, 可以得到以下合并公式:

  • lenx=lenls+lenrs

    • 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(lenxi+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)×iT+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);
              }
          }
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值