bzoj 2752
一道看似是期望,其实与期望关系并不是特别大的题...
首先分析一下题意:
虽然题目中提到的是边,但事实上完全可以把每一条边的贡献放进左端点上(因为无论从哪个方向经过这条边都是计算左端点的代价)
(题目中的提示多么明显啊!!!收费站显然是按点收费嘛...)
因此,在修改区间$[l,r]$的边权时,我们事实上修改的是区间$[l,r-1]$的点权!
这不就是个线段树了嘛
可是...怎么算期望呢?
首先把式子列出来:设我们要求的区间是$[l,r]$,直接计算整个区间的期望有点困难,我们单独讨论每个点的贡献,那么对任意$p\in [l,r]$,设这个点的代价为$v_{p}$,有:
$E(p)=\frac{(p-l+1)*(r-p)*v_{p}}{C_{r-l+1}^{2}}$
那么整个区间的期望就是:
$\sum_{i=l}^{r}E(i)$
首先发现分母是定值,可以直接提出来,那么我们直接研究这个表达式就可以:
$\sum_{i=l}^{r}(i-l+1)*(r-i)*v_{i}$
发现就整个区间而言,这并不太好维护,因此我们展开上述表达式:
$\sum_{i=l}^{r}(l+r-1)*i*v_{i}-\sum_{i=l}^{r}i^{2}*v_{i}-\sum_{i=l}^{r}*(l*r-r)*v_{i}$
这个表达式里面所有与$l,r$有关项的都被提了出来作为系数,直接维护后面的值即可
因此我们要维护的东西就变成了
$\sum v_{i}$,$\sum i*v_{i}$与$\sum i^{2}*v_{i}$
这三个东西都可以用线段树搞定嘛
需要用到一个公式:
$\sum_{i=1}^{n}i^{2}=\frac{n*(n+1)*(2n+1)}{6}$
这就完事了
(网上大部分的题解给出的表达式会长这样:$E(p)=\frac{(p-l+1)*(r-p+1)*v_{p}}{C_{r-l+2}^{2}}$,然后每次询问$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; }