【康娜的线段树】

挺妙的一道期望题

首先经过一番简单的思考就会发现对于线段树上的一个叶子节点\(x\),深度为\(deep[x]\),那么走到这个节点的概率就是\(2^{deep[x]}\)

我们设\(val[x]\)表示叶节点\(x\)到根经过的所有节点的权值和为\(val[x]\)

于是最后的答案就是

\[qwq*\sum_{i=1}^{n}\frac{val[i]}{2^{deep[i]}}\]

连分母都不一样,于是先通分一下吧

\[qwq*\sum_{i=1}^n\frac{val[i]*2^{maxdep-deep[i]}}{2^{maxdep}}\]

但是要是遇上了区间修改呢,我们完全没有办法维护\(val[i]\)

好像遇上了瓶颈

于是我们换一个思路,对整棵线段树来考虑,在算概率的时候,显然树上有很多节点是被多次计入答案的

比如对于线段树上的节点\([1,n]\)每一个点的\(val\)都算入了这个点

这具有非常妙的性质

我们暂且将分子上的\(2^{maxdep-deep[i]}\)称为叶节点的权,那么一个线段树上的非叶节点的总权值就是其内部所有叶子节点的权的和,就是子树和

那么这个子树和显然就表示在最终计入答案的时候,这个线段树上的节点管辖的区间和应该乘上多少次

那么我们现在的答案就可以写成

\[qwq*\sum_{i=1}^M\frac{sum[i]*d[i]}{2^{maxdep}}\]

\(sum[i]\)表示子树和,\(d[i]\)表示线段树上的节点\(i\)所管辖的区间和,\(M\)为线段树的节点个数

现在再来考虑一下修改一个节点会对整个线段树产生什么样的影响

显然就是从这个节点一直往上直到根所有节点的管辖的区间和都会相应的增加

我们设\(pre[i]\)(可以理解为根路径前缀和)表示\(i\)这个节点到根节点经过的所有节点的\(sum\)的和为多少,如果这次修改的增量为\(k\),那么对于整个线段树的答案的贡献就是\(pre[i]*k\)

之后就会发现好像每一个单点之间的修改互不影响,于是如果对一个区间\([x,y]\)进行修改的话对答案的贡献就是\(k*\sum_{i=x}^ypre[i]\)

于是我们直接对\(pre\)数组做出一个前缀和就可以快速回答了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define maxn 1000005
inline int read()
{
    re char c=getchar();
    re int x=0,r=1;
    while(c<'0'||c>'9') 
    {
        if(c=='-') r=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
        x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x*r;
} 
inline void write(LL x)
{
    if(x>9) write(x/10);
    putchar(x%10+48);
}
int a[maxn];
int deep[maxn];
int p[maxn],val[maxn];
LL sum[maxn<<2],pre[maxn];
int n,Q,qwq,maxdep;
LL ans,FM;
void build(int x,int y,int dep,LL Sum)//求出所有叶节点的深度,和val
{
    if(x==y)
    {
        deep[x]=dep;
        val[x]=Sum;
        maxdep=max(maxdep,dep);
        return;
    }
    int mid=x+y>>1;
    build(x,mid,dep+1,Sum+p[mid]-p[x-1]),build(mid+1,y,dep+1,Sum+p[y]-p[mid]);
}
LL dfs(int x,int y,int i)//求出线段树上所有点的子树和
{
    if(x==y) return sum[i]=1ll<<(maxdep-deep[x]);
    int mid=x+y>>1;
    return sum[i]=dfs(x,mid,i<<1)+dfs(mid+1,y,i<<1|1);
}
void Pre_Dfs(int x,int y,int i,LL Sum)//求出每一个节点的pre值
{
    if(x==y)
    {
        pre[x]=Sum;
        return;
    }
    int mid=x+y>>1;
    Pre_Dfs(x,mid,i<<1,Sum+sum[i<<1]),Pre_Dfs(mid+1,y,i<<1|1,Sum+sum[i<<1|1]);
}
inline LL gcd(LL a,LL b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
int main()
{
    n=read(),Q=read(),qwq=read();
    for(re int i=1;i<=n;i++) a[i]=read(),p[i]=p[i-1]+a[i];
    build(1,n,0,p[n]);
    dfs(1,n,1);
    Pre_Dfs(1,n,1,sum[1]);
    for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+pre[i];
    for(re int i=1;i<=n;i++) ans+=val[i]*(1ll<<(maxdep-deep[i]));//先求出最开始的答案
    int x,y,z;
    FM=1ll<<maxdep;
    LL r=gcd(qwq,FM);
    qwq/=r,FM/=r;//约分,防止爆long long
    while(Q--)
    {
        x=read(),y=read(),z=read();
        ans+=(pre[y]-pre[x-1])*z;
        if(ans>0) write(qwq*ans/FM);
        else putchar('-'),write(-qwq*ans/FM);
        putchar(10);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/asuldb/p/10206191.html
今日推荐