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;
}

 

posted @ 2019-05-30 21:20  lleozhang  Views(175)  Comments(1Edit  收藏  举报
levels of contents