bzoj 2752
一道看似是期望,其实与期望关系并不是特别大的题...
首先分析一下题意:
虽然题目中提到的是边,但事实上完全可以把每一条边的贡献放进左端点上(因为无论从哪个方向经过这条边都是计算左端点的代价)
(题目中的提示多么明显啊!!!收费站显然是按点收费嘛...)
因此,在修改区间的边权时,我们事实上修改的是区间的点权!
这不就是个线段树了嘛
可是...怎么算期望呢?
首先把式子列出来:设我们要求的区间是,直接计算整个区间的期望有点困难,我们单独讨论每个点的贡献,那么对任意,设这个点的代价为,有:
那么整个区间的期望就是:
首先发现分母是定值,可以直接提出来,那么我们直接研究这个表达式就可以:
发现就整个区间而言,这并不太好维护,因此我们展开上述表达式:
这个表达式里面所有与有关项的都被提了出来作为系数,直接维护后面的值即可
因此我们要维护的东西就变成了
,与
这三个东西都可以用线段树搞定嘛
需要用到一个公式:
这就完事了
(网上大部分的题解给出的表达式会长这样:,然后每次询问,这两种做法是完全等价的,但是个人认为我的公式更好理解,因为这个点本身当做终点是不会产生任何贡献的,因此不必进行也可以搞出正确的公式)
贴代码:
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· 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