[HAOI2012]高速公路(road)

Description
Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。
Y901高速公路是一条由 N1 段路以及 N 个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1N,从收费站 i 行驶到i+1(或从 i+1 行驶到 i )需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。
政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。
无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的 l,r(l<r) ,在第 l 个到第r个收费站里等概率随机取出两个不同的收费站 a b,那么从 a 行驶到b将期望花费多少费用呢?

Input
第一行 2 个正整数N,M,表示有 N 个收费站,M次调整或询问
接下来 M 行,每行将出现以下两种形式中的一种:
C l r v 表示将第 l 个收费站到第r个收费站之间的所有道路的通行费全部增加 v
Q l r 表示对于给定的l,r,要求回答小A的问题。
所有C与Q操作中保证 1<=l<r<=N

Output
对于每次询问操作回答一行,输出一个既约分数。
若答案为整数 a ,输出a/1

Sample Input
4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4

Sample Output
1/1
8/3
17/6

HINT

数据规模
所有C操作中的 v 的绝对值不超过10000
在任何时刻任意道路的费用均为不超过 10000 的非负整数。
所有测试点的详细情况如下表所示:

TestNM
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

Source

思路
纯数学题啊……考虑一条道路 i 对答案的贡献(记为ansi),设这条道路的权值为 vi ,那么记

{vii=viiviii=vii2
ansi=vi[(il+1)(ri)]=vi(irlr+ri2+ili)=viirvilr+virviii+viilvii=vi(lr+r)+vii(l+r1)viii
则有
ans=i=lr1vi(lr+r)+i=lr1vii(l+r1)i=lr1viii
那么只要维护 vi 之和, vii 之和, viii 之和就可以了。而当将一些范围为 lr 的道路增加 q ,那么:
v+=q(rl+1)vi+=q(l+r)(rl+1)2vii+=qr(r+1)(2r+1)l(l+1)(2l+1)6
具体怎么证在这里就不讲了,自己去上网搜吧。

代码

#include <cstdio>

const int maxn=100000;

struct data
{
  long long v,vi,vii;

  data operator +=(const data &other)
  {
    v+=other.v;
    vi+=other.vi;
    vii+=other.vii;
    return *this;
  }

  data(long long vv=0,long long vvi=0,long long vvii=0):v(vv),vi(vvi),vii(vvii){}
};
struct segment_tree
{
  long long v[(maxn<<2)+10],vi[(maxn<<2)+10],vii[(maxn<<2)+10],lazy[(maxn<<2)+10];

  long long updata(long long now)
  {
    v[now]=v[now<<1]+v[now<<1|1];
    vi[now]=vi[now<<1]+vi[now<<1|1];
    vii[now]=vii[now<<1]+vii[now<<1|1];
    return 0;
  }

  long long push(long long now,long long left,long long right,long long q)
  {
    v[now]+=q*(right-left+1);
    vi[now]+=(q*(left+right)*(right-left+1))>>1;
    vii[now]+=q*(right*(right+1)*((right<<1)+1)-(left-1)*left*((left<<1)-1))/6;
    lazy[now]+=q;
    return 0;
  }

  long long pushdown(long long now,long long left,long long right)
  {
    if(lazy[now])
      {
        long long mid=(left+right)>>1;
        push(now<<1,left,mid,lazy[now]);
        push(now<<1|1,mid+1,right,lazy[now]);
        lazy[now]=0;
      }
    return 0;
  }

  long long build(long long now,long long left,long long right)
  {
    if(left==right)
      {
        lazy[now]=v[now]=vi[now]=vii[now]=0;
        return 0;
      }
    lazy[now]=v[now]=vi[now]=vii[now]=0;
    long long mid=(left+right)>>1;
    build(now<<1,left,mid);
    build(now<<1|1,mid+1,right);
    return 0;
  }

  long long add(long long now,long long left,long long right,long long askl,long long askr,long long cval)
  {
    if((askl<=left)&&(right<=askr))
      {
        push(now,left,right,cval);
        return 0;
      }
    long long mid=(left+right)>>1;
    pushdown(now,left,right);
    if(askl<=mid)
      {
        add(now<<1,left,mid,askl,askr,cval);
      }
    if(mid<askr)
      {
        add(now<<1|1,mid+1,right,askl,askr,cval);
      }
    updata(now);
    return 0;
  }

  data query(long long now,long long left,long long right,long long askl,long long askr)
  {
    if((askl<=left)&&(right<=askr))
      {
        return data(v[now],vi[now],vii[now]);
      }
    pushdown(now,left,right);
    long long mid=(left+right)>>1;
    data res=data();
    if(askl<=mid)
      {
        res+=query(now<<1,left,mid,askl,askr);
      }
    if(mid<askr)
      {
        res+=query(now<<1|1,mid+1,right,askl,askr);
      }
    return res;
  }
};

long long n,m,a,b,c;
char s[10];
segment_tree st;
data ans;

long long gcd(long long a,long long b)
{
  if(!b)
    {
      return a;
    }
  else
    {
      return gcd(b,a%b);
    }
}

int main()
{
  scanf("%lld%lld",&n,&m);
  --n;
  st.build(1,1,n);
  while(m--)
    {
      scanf("%s%I64d%I64d",s,&a,&b);
      if(s[0]=='C')
        {
          scanf("%lld",&c);
          st.add(1,1,n,a,b-1,c);
        }
      else
        {
          ans=st.query(1,1,n,a,b-1);
          long long t=ans.v*(-a*b+b)+ans.vi*(a+b-1)-ans.vii;
          if(!t)
            {
              puts("0/1");
            }
          else
            {
              long long m=((b-a)*(b-a+1))>>1,g=gcd(t,m);
              printf("%lld/%lld\n",t/g,m/g);
            }
        }
    }
  return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值