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

题目:

我是超链接

题解:

这个题目期望的点就是把每一段的距离加起来然后/[(r-l+1)*(r-l)/2]
那么这道题目就剩下求每个子区间的和 的和
这个1e5的范围还有修改操作我们选择线段树
很明显权值是在边上的,这里我们已经考虑r–了,那么我们要求的值=Σcost[i]* (i-l+1)*(r-i+1)
其实并不难理解,就是包含这个数的左子集*包含这个数的右子集
然后拆开=(r-l+1-rl)Σcost[i]+(l+r)Σi*cost[i]-Σi*i*cost[i]
那么我们只需要用线段树维护Σcost[i],Σi*cost[i],Σi*i*cost[i]就好了
修改的时候随便画画柿子pushdown就行

代码:

#include <cstdio>
#include <cstring>
#define LL unsigned long long
using namespace std;
const int N=100005;
LL delta[N*4],sum[N*4],sum1[N*4],sum2[N*4],s,s1,s2;
LL pfh(LL x){return x*(x+1)*(2*x+1)/6;}
LL gcd(LL a,LL b){if (!b) return a;else return gcd(b,a%b);}
void updata(int now)
{
    sum[now]=sum[now<<1]+sum[now<<1|1];
    sum1[now]=sum1[now<<1]+sum1[now<<1|1];
    sum2[now]=sum2[now<<1]+sum2[now<<1|1];
}
void pushdown(int now,int l,int r,int mid)
{
    if (delta[now])
    {
        sum[now<<1]+=(LL)(mid-l+1)*delta[now];
        sum[now<<1|1]+=(LL)(r-mid)*delta[now];
        sum1[now<<1]+=(LL)(l+mid)*(LL)(mid-l+1)/2LL*delta[now];
        sum1[now<<1|1]+=(LL)(r+mid+1)*(LL)(r-mid)/2LL*delta[now];
        sum2[now<<1]+=(pfh(mid)-pfh(l-1))*delta[now];
        sum2[now<<1|1]+=(pfh(r)-pfh(mid))*delta[now];
        delta[now<<1]+=delta[now];
        delta[now<<1|1]+=delta[now];
        delta[now]=0;
    }
}
void add(int now,int l,int r,int lrange,int rrange,LL v)
{
    if (lrange<=l && rrange>=r) 
    {
        sum[now]+=(LL)(r-l+1)*(LL)v; delta[now]+=v;
        sum1[now]+=(LL)(l+r)*(LL)(r-l+1)/2*v;
        sum2[now]+=(pfh(r)-pfh(l-1))*v;
        return;
    }
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) add(now<<1,l,mid,lrange,rrange,v);
    if (rrange>mid) add(now<<1|1,mid+1,r,lrange,rrange,v);
    updata(now);
}
void qurry(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r) {s+=sum[now];s1+=sum1[now];s2+=sum2[now];return;}
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) qurry(now<<1,l,mid,lrange,rrange);
    if (rrange>mid) qurry(now<<1|1,mid+1,r,lrange,rrange);
}
int main()
{
    int n,m,l,r;LL v;scanf("%d%d",&n,&m);
    while (m--)
    {
        char st[5];s=s1=s2=0;
        scanf("%s",st);scanf("%d%d",&l,&r);
        LL fm=(LL)(r-l+1)*(LL)(r-l)/2;r--;
        if (st[0]=='C')
        {
            scanf("%lld",&v);
            add(1,1,n,l,r,v);
        }else
        {
            qurry(1,1,n,l,r);
            LL fz=(LL)(r-l+1-(LL)r*l)*s-s2+(LL)(l+r)*s1;
            LL d=gcd(fm,fz);fm/=d;fz/=d;
            printf("%llu/%llu\n",fz,fm);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值