洛谷 P3924 康娜的线段树 解题报告

时间:2022-12-31 07:26:01

P3924 康娜的线段树

题目描述

小林是个程序媛,不可避免地康娜对这种人类的“魔法”产生了浓厚的兴趣,于是小林开始教她\(OI\)。

洛谷 P3924 康娜的线段树 解题报告

今天康娜学习了一种叫做线段树的神奇魔法,这种魔法可以维护一段区间的信息,是非常厉害的东西。康娜试着写了一棵维护区间和的线段树。由于她不会打标记,因此所有的区间加操作她都是暴力修改的。具体的代码如下:

struct Segment_Tree{
#define lson (o<<1)
#define rson (o<<1|1)
int sumv[N<<2],minv[N<<2];
inline void pushup(int o){sumv[o]=sumv[lson]+sumv[rson];}
inline void build(int o,int l,int r){
if(l==r){sumv[o]=a[l];return;}
int mid=(l+r)>>1;
build(lson,l,mid);build(rson,mid+1,r);
pushup(o);
}
inline void change(int o,int l,int r,int q,int v){
if(l==r){sumv[o]+=v;return;}
int mid=(l+r)>>1;
if(q<=mid)change(lson,l,mid,q,v);
else change(rson,mid+1,r,q,v);
pushup(o);
}
}T;

在修改时,她会这么写:

for(int i=l;i<=r;i++)T.change(1,1,n,i,addv);

显然,这棵线段树每个节点有一个值,为该节点管辖区间的区间和。

康娜是个爱思考的孩子,于是她突然想到了一个问题:

如果每次在线段树区间加操作做完后,从根节点开始等概率的选择一个子节点进入,直到进入叶子结点为止,将一路经过的节点权值累加,最后能得到的期望值是多少?

康娜每次会给你一个值\(qwq\),保证你求出的概率乘上\(qwq\)是一个整数。

这个问题太简单了,以至于聪明的康娜一下子就秒了。

现在她想问问你,您会不会做这个题呢?

输入输出格式

输入格式:

第一行整数\(n,m,qwq\)表示线段树维护的原序列的长度,询问次数,分母。

第二行\(n\)个数,表示原序列。

接下来\(m\)行,每行三个数\(l,r,x\)表示对区间\([l,r]\)加上\(x\)

输出格式:

共\(m\)行,表示期望的权值和乘上\(qwq\)结果。

说明

对于30%的数据,保证 \(1 \leq n,m \leq 100\)

对于70%的数据,保证 \(1 \leq n,m, \leq 10^{5}\)

对于100%的数据,保证 \(1 \leq n,m \leq 10^6\)

\(-1000 \leq a_i,x \leq 1000\)


其实题目不难,然而我概率期望学的差,还是不怎么会做。

我们发现,其实每个叶子节点的贡献的不会变的,则第\(i\)个叶子节点贡献的次数是它之前的所有包含它的区间的贡献次数之和。

根据条件概率,每一个大区间出现的概率都是它的子区间的两倍,所以我们以最小的区间算做1,统计每个叶子节点的贡献次数,最后再除以\(\lceil logn \rceil\)即可。

具体实现可以直接模拟建树统计。

然后我们发现操作只有区间加和全局询问。

区间加我们可以通过叶子节点贡献次数前缀和维护全局偏移量。

复杂度:\(O(nlogn+m)\)


Code:

#include <cstdio>
#define ll long long
ll max(ll x,ll y){return x>y?x:y;}
const ll N=1000010;
ll dat[N],cnt[N],f[N],ans,QAQ,n,m,d,dep[N];
void build(ll l,ll r,ll Dep)
{
if(l==r)
{
dep[l]=Dep;
d=max(d,Dep);
return;
}
ll mid=l+r>>1;
build(l,mid,Dep+1);
build(mid+1,r,Dep+1);
}
void init()
{
scanf("%lld%lld%lld",&n,&m,&QAQ);
build(1,n,1);
for(ll i=1;i<=n;i++)
{
if(dep[i]==d)
cnt[i]=(1<<d)-1;
else
cnt[i]=(1<<d)-2;
scanf("%lld",dat+i);
f[i]=f[i-1]+cnt[i];
ans+=cnt[i]*dat[i];
}
}
void work()
{
ll l,r,x;
d=1<<d-1;
for(ll i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&l,&r,&x);
ans+=(f[r]-f[l-1])*x;
printf("%lld\n",(QAQ/d*ans));
}
}
int main()
{
init();
work();
return 0;
}

2018.7.21