bzoj 2752

一道看似是期望,其实与期望关系并不是特别大的题...

首先分析一下题意:

虽然题目中提到的是边,但事实上完全可以把每一条边的贡献放进左端点上(因为无论从哪个方向经过这条边都是计算左端点的代价)

(题目中的提示多么明显啊!!!收费站显然是按点收费嘛...)

因此,在修改区间[l,r]的边权时,我们事实上修改的是区间[l,r1]的点权!

这不就是个线段树了嘛

可是...怎么算期望呢?

首先把式子列出来:设我们要求的区间是[l,r],直接计算整个区间的期望有点困难,我们单独讨论每个点的贡献,那么对任意p[l,r],设这个点的代价为vp,有:

E(p)=(pl+1)(rp)vpCrl+12

那么整个区间的期望就是:

i=lrE(i)

首先发现分母是定值,可以直接提出来,那么我们直接研究这个表达式就可以:

i=lr(il+1)(ri)vi

发现就整个区间而言,这并不太好维护,因此我们展开上述表达式:

i=lr(l+r1)ivii=lri2vii=lr(lrr)vi

这个表达式里面所有与l,r有关项的都被提了出来作为系数,直接维护后面的值即可

因此我们要维护的东西就变成了

vi,ivii2vi

这三个东西都可以用线段树搞定嘛

需要用到一个公式:

i=1ni2=n(n+1)(2n+1)6

这就完事了

(网上大部分的题解给出的表达式会长这样:E(p)=(pl+1)(rp+1)vpCrl+22,然后每次询问r,这两种做法是完全等价的,但是个人认为我的公式更好理解,因为这个点本身当做终点是不会产生任何贡献的,因此不必进行r也可以搞出正确的公式)

贴代码:

复制代码
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define rt1 rt<<1
#define rt2 (rt<<1)|1
using namespace std;
struct Seg_tree
{
    ll val_sum;
    ll sig_sum;
    ll sqr_sum;
    ll lazy;
}tree[400005];
char ch[5];
int n,m;
void pushdown(int rt,int l,int r)
{
    int mid=(l+r)>>1;
    tree[rt1].val_sum+=1ll*(mid-l+1)*tree[rt].lazy;
    tree[rt2].val_sum+=1ll*(r-mid)*tree[rt].lazy;
    tree[rt1].sig_sum+=(l+mid)*1ll*(mid-l+1)/2*1ll*tree[rt].lazy;
    tree[rt2].sig_sum+=1ll*(mid+1+r)*(r-mid)/2*tree[rt].lazy;
    tree[rt1].sqr_sum+=tree[rt].lazy*(mid*1ll*(mid+1)*(2ll*mid+1)/6-(l-1)*1ll*l*(2ll*l-1)/6);
    tree[rt2].sqr_sum+=tree[rt].lazy*(r*1ll*(r+1)*(2ll*r+1)/6-mid*1ll*(mid+1)*(2ll*mid+1)/6);
    tree[rt1].lazy+=tree[rt].lazy;
    tree[rt2].lazy+=tree[rt].lazy;
    tree[rt].lazy=0;
}
void pushup(int rt)
{
    tree[rt].val_sum=tree[rt1].val_sum+tree[rt2].val_sum;
    tree[rt].sig_sum=tree[rt1].sig_sum+tree[rt2].sig_sum;
    tree[rt].sqr_sum=tree[rt1].sqr_sum+tree[rt2].sqr_sum;
}
void update(int rt,int l,int r,int lq,int rq,ll w)
{
    if(l>=lq&&r<=rq)
    {
        tree[rt].val_sum+=w*(r-l+1);
        tree[rt].sig_sum+=w*(1ll*l+r)*1ll*(r-l+1)/2;
        tree[rt].sqr_sum+=w*(1ll*r*1ll*(r+1)*(2ll*r+1)/6-1ll*(l-1)*1ll*l*(2ll*l-1)/6);
        tree[rt].lazy+=w;
        return;
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if(lq<=mid)update(rt1,l,mid,lq,rq,w);
    if(rq>mid)update(rt2,mid+1,r,lq,rq,w);
    pushup(rt);
}
Seg_tree query(int rt,int l,int r,int lq,int rq)
{
    if(l>=lq&&r<=rq)return tree[rt];
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    Seg_tree ret=(Seg_tree){0,0,0,0};
    if(lq<=mid)
    {
        Seg_tree temp=query(rt1,l,mid,lq,rq);
        ret.val_sum+=temp.val_sum;
        ret.sig_sum+=temp.sig_sum;
        ret.sqr_sum+=temp.sqr_sum;
    }
    if(rq>mid)
    {
        Seg_tree temp=query(rt2,mid+1,r,lq,rq);
        ret.val_sum+=temp.val_sum;
        ret.sig_sum+=temp.sig_sum;
        ret.sqr_sum+=temp.sqr_sum;
    }
    return ret;
}
ll gcd(ll x,ll y)
{
    return y?gcd(y,x%y):x;
}
int main()
{
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%s",ch);
        if(ch[0]=='C')
        {
            int l,r;
            ll w;
            scanf("%d%d%lld",&l,&r,&w);
            r--;
            update(1,1,n,l,r,w);
        }else
        {
            int l,r;
            scanf("%d%d",&l,&r);
            Seg_tree ans=query(1,1,n,l,r);
            ll mot=1ll*(r-l+1)*1ll*(r-l)/2;
            ll son=1ll*(l+r-1)*ans.sig_sum-1ll*(1ll*l*r-r)*ans.val_sum-ans.sqr_sum;
            ll d=gcd(mot,son);
            mot/=d,son/=d;
            printf("%lld/%lld\n",son,mot);
        }
    }
    return 0;
}
复制代码

 

posted @   lleozhang  Views(178)  Comments(1Edit  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
levels of contents
点击右上角即可分享
微信分享提示