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