LuoguP3924 康娜的线段树 期望+线段树

根据期望的定义,我们可以求出所有情况之和再除以情况数量. 

如果长度满足 $n=2^k$,线段树上一个节点新加 $v$ 的话 $v$ 的贡献就是 $v \times si[x]$,si[x] 即子树下叶节点个数.     

如果长度不满足上述条件,由于线段树是完全二叉树结构,我们可以强制让深度小于最大深度的叶节点多一个贡献。    

如果单点修改,该点的贡献就是叶节点到根节点所有节点 $si[x]$ 之和乘以 $v$,然后区间修改的话可以用前缀和来算区间的贡献.  

时间复杂度为 $O(4n)+O(n)=O(n)$. 

code: 

#include <cstdio> 
#include <algorithm> 
#include <cstring>   
#define ll long long 
#define N 1000009    
#define lson now<<1  
#define rson now<<1|1   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
ll Q;    
ll sum[N],val[N<<2];   
int dep[N<<2],si[N<<2],a[N],n,m,maxdep;    
void build(int l,int r,int now)  {  
    dep[now]=dep[now>>1]+1;     
    if(l==r) {     
        maxdep=max(maxdep,dep[now]);   
        if(dep[now]<maxdep) {   
            si[now]=2;       
        } 
        else si[now]=1;  
        return; 
    } 
    int mid=(l+r)>>1;  
    build(l,mid,lson),build(mid+1,r,rson);     
    si[now]=si[lson]+si[rson];              
}
void dfs2(int l,int r,int now) {  
    val[now]=si[now]+val[now>>1];      
    if(l==r) { sum[l]=val[now]; return; }     
    int mid=(l+r)>>1;  
    dfs2(l,mid,lson),dfs2(mid+1,r,rson);     
}
int main() {  
    // setIO("input");    
    scanf("%d%d%lld",&n,&m,&Q);      
    build(1,n,1);     
    dfs2(1,n,1);  
    for(int i=1;i<=n;++i) sum[i]+=sum[i-1];  
    ll ans=0; 
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),ans+=(sum[i]-sum[i-1])*a[i];    
    int x,y,z; 
    ll dn=si[1];
    ll gcd=__gcd(dn,Q);         
    dn/=gcd,Q/=gcd;    
    for(int i=1;i<=m;++i) {  
        scanf("%d%d%d",&x,&y,&z);   
        ans+=(sum[y]-sum[x-1])*1ll*z;       
        printf("%lld\n",ans/dn*Q);   
    }     
    return 0;   
}      

  

posted @ 2020-07-09 07:53  EM-LGH  阅读(165)  评论(0编辑  收藏  举报