bzoj 2752: [HAOI2012]高速公路(road) (线段树)

题目描述

传送门

题目大意:一个n个点的链,有n-1条路径,每条路径有一个花费。会不定期的对连续路段的花费进行区间修改。
对于给定的 l,r(l<r) ,在 [l,r] 中任选两个点a,b,那么从a到b将期望花费多少费用呢?

题解

对于路径建立线段树,所以一共有n-1个点。
维护的话有点恶心。
cnt=(r-l+1) 区间点数
size 表示任选两个点有多少种选择
tr 表示区间权值和
delta 区间修改的标记
sum 从区间中任选两个点a,b,所有方案的总花费
ls 区间中的每个点到左端点的路径和
rs 区间中的每个点到右端点的路径和
具体的更新过程还是看代码吧,不是很好说明。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100003
#define LL long long
using namespace std;
struct data{
    LL cnt,size,sum,tr,ls,rs,delta;
}a[N*4];
int n,m;
LL base[N];
data update(data l,data r)
{
    data c; c.delta=0;
    c.cnt=l.cnt+r.cnt;
    c.size=l.size+r.size+l.cnt*r.cnt;
    c.tr=l.tr+r.tr;
    c.sum=l.sum+r.sum+l.rs*r.cnt+r.ls*l.cnt;
    c.rs=l.rs+r.rs+l.cnt*r.tr;
    c.ls=l.ls+r.ls+r.cnt*l.tr;
    return c;
}
void build(int now,int l,int r)
{
    if (l==r) {
        a[now].size=a[now].cnt=1;
        return;
    }
    int mid=(l+r)/2;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    a[now]=update(a[now<<1],a[now<<1|1]);
}
LL calc(LL x){
    return x*(x+1)/2;
}
void change(int x,int l,int r,LL val)
{
    a[x].tr+=a[x].cnt*val;
    a[x].sum+=base[a[x].cnt]*val;
    a[x].ls+=calc(a[x].cnt)*val;
    a[x].rs+=calc(a[x].cnt)*val;
    a[x].delta+=val;
}
void pushdown(int now,int l,int r)
{
    int mid=(l+r)/2;
    if (a[now].delta) {
        change(now<<1,l,mid,a[now].delta); 
        change(now<<1|1,mid+1,r,a[now].delta);
        a[now].delta=0;
    }
}
void qjchange(int now,int l,int r,int ll,int rr,LL val)
{
    if (ll>rr) return;
    if (ll<=l&&r<=rr) {
        change(now,l,r,val);
        return;
    }
    int mid=(l+r)/2;
    pushdown(now,l,r);
    if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,val);
    if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,val);
    a[now]=update(a[now<<1],a[now<<1|1]);
}
data qjsum(int now,int l,int r,int ll,int rr)
{
    if (ll>rr) {
        data ans; ans.sum=0;
        return ans;
    }
    if (ll<=l&&r<=rr) return a[now];
    int mid=(l+r)/2;
    pushdown(now,l,r);
    data ans; ans.cnt=0;
    if (ll<=mid) ans=qjsum(now<<1,l,mid,ll,rr);
    if (rr>mid) {
        if (ans.cnt==0) ans=qjsum(now<<1|1,mid+1,r,ll,rr);
        else ans=update(ans,qjsum(now<<1|1,mid+1,r,ll,rr));
    }
    return ans;
}
LL gcd(LL x,LL y)
{
    LL r;
    while (y) {
        r=x%y;
        x=y; y=r;
    }
    return x;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m);
    n--;
    base[1]=1;
    for (int i=2;i<=n;i++)
     base[i]=base[i-1]+calc(i-1)+(LL)i;
    build(1,1,n);
    for (int i=1;i<=m;i++) {
        char s[10]; int x,y; scanf("%s%d%d",s+1,&x,&y);
        y--;
        if (s[1]=='C') {
            int val; scanf("%d",&val);
            qjchange(1,1,n,x,y,val);
        }
        else {
            data ans=qjsum(1,1,n,x,y);
            LL a=ans.sum; LL b=ans.size;
            LL t=gcd(a,b);
            a/=t; b/=t;
            printf("%lld/%lld\n",a,b);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值