bzoj2752 [HAOI2012]高速公路(road) 线段树

90 篇文章 0 订阅

Description


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

所有C操作中的v的绝对值不超过10000
在任何时刻任意道路的费用均为不超过10000的非负整数
100% n=100000 m=100000

Solution


期望实际上就是 ri=l(il+1)(ri+1)v[i](2rl+1) ∑ i = l r ( i − l + 1 ) ( r − i + 1 ) v [ i ] ( 2 r − l + 1 ) ,拆开算贡献已经是常规操作
大概是这样的一个东西,根据求和里面与i的关系拆开来有三项
上线段树分别维护 v[i] ∑ v [ i ] v[i]i ∑ v [ i ] ∗ i v[i]i2 ∑ v [ i ] ∗ i 2 即可

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int N=400000;

struct data {
    LL s1,s2,s3;

    data operator +(data b) const {
        return (data) {s1+b.s1,s2+b.s2,s3+b.s3};
    }
} ;

LL s1[N*4],s2[N*4],s3[N*4];
LL tag[N*4];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

LL calc1(LL l,LL r) {
    return ((r+1)*r-(l-1)*l)/2;
}

LL calc2(LL l,LL r) {
    return r*(r+1)*(2*r+1)/6-(l-1)*l*(2*l-1)/6;
}

void push_down(int now,int l,int mid,int r) {
    if (!tag[now]) return ;
    LL w=tag[now]; tag[now]=0;
    tag[now<<1]+=w; tag[now<<1|1]+=w;

    s1[now<<1]+=(mid-l+1)*w;
    s1[now<<1|1]+=(r-mid)*w;

    s2[now<<1]+=calc1(l,mid)*w;
    s2[now<<1|1]+=calc1(mid+1,r)*w;

    s3[now<<1]+=(LL)w*calc2(l,mid);
    s3[now<<1|1]+=(LL)w*calc2(mid+1,r);
}

void modify(int now,int tl,int tr,int l,int r,LL v) {
    if (tl==l&&tr==r) {
        tag[now]+=v;
        s1[now]+=v*(r-l+1);
        s2[now]+=v*calc1(l,r);
        s3[now]+=v*calc2(l,r);
        return ;
    }
    int mid=(tl+tr)>>1;
    push_down(now,tl,mid,tr);
    if (r<=mid) modify(now<<1,tl,mid,l,r,v);
    else if (l>mid) modify(now<<1|1,mid+1,tr,l,r,v);
    else {
        modify(now<<1,tl,mid,l,mid,v);
        modify(now<<1|1,mid+1,tr,mid+1,r,v);
    }
    s1[now]=s1[now<<1]+s1[now<<1|1];
    s2[now]=s2[now<<1]+s2[now<<1|1];
    s3[now]=s3[now<<1]+s3[now<<1|1];
}

data query(int now,int tl,int tr,int l,int r) {
    if (tl==l&&tr==r) return (data) {s1[now],s2[now],s3[now]};
    int mid=(tl+tr)>>1;
    push_down(now,tl,mid,tr);
    if (r<=mid) return query(now<<1,tl,mid,l,r);
    if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
    data qx=query(now<<1,tl,mid,l,mid);
    data qy=query(now<<1|1,mid+1,tr,mid+1,r);
    return qx+qy;
}

LL gcd(LL x,LL y) {
    return !y?x:gcd(y,x%y);
}

int main(void) {
    int n=read();
    for (int T=read();T--;) {
        char opt[2]; scanf("%s",opt);
        if (opt[0]=='C') {
            int l=read(),r=read()-1,v=read();
            modify(1,1,n-1,l,r,v);
        } else if (opt[0]=='Q') {
            int l=read(),r=read()-1;
            data ret=query(1,1,n-1,l,r);
            LL ans1=ret.s1*(LL)(r-l-(LL)r*l+1)+ret.s2*(LL)(r+l)-ret.s3;
            LL ans2=(LL)(r-l+2)*(LL)(r-l+1)/2;
            LL GCD=gcd(ans1,ans2);
            printf("%lld/%lld\n", ans1/GCD,ans2/GCD);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值