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

浙公网安备 33010602011771号