小林是个程序媛,不可避免地康娜对这种人类的“魔法”产生了浓厚的兴趣,于是小林开始教她OI。
今天康娜学习了一种叫做线段树的神奇魔法,这种魔法可以维护一段区间的信息,是非常厉害的东西。康娜试着写了一棵维护区间和的线段树。由于她不会打标记,因此所有的区间加操作她都是暴力修改的。具体的代码如下:
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);
显然,这棵线段树每个节点有一个值,为该节点管辖区间的区间和。
康娜是个爱思考的孩子,于是她突然想到了一个问题:
如果每次在线段树区间加操作做完后,从根节点开始等概率的选择一个子节点进入,直到进入叶子结点为止,将一路经过的节点权值累加,最后能得到的期望值是多少?
1 ≤ n , m ≤ 1 0 6 1 \leq n,m \leq 10^6 1≤n,m≤106
− 1000 ≤ a i , x ≤ 1000 -1000 \leq a_i,x \leq 1000 −1000≤ai,x≤1000
对于本题,我们有一个最朴素的想法就是对每一层求一个和,乘上到达这一层的概率,即 1 2 d e p − 1 \frac{1}{2^{dep-1}} 2dep−11,但这样是无法比较快速的应对修改操作的
基于这种思路,我们换个思考的方向,即思考一个 i i i
显然包含 i i i 的块是连续的,如下图:
对于包含 2 2 2 下标的块,一定是连续的
而他所做的贡献就是上图标注的
所以现在我们只需要知道每一个下标 i , i ∈ [ 1 , n ] i,i\in [1,n] i,i∈[1,n],它对应的叶节点的深度是多少
所以为了实现这一目标,我们来单独去求这个东西:
求深度是可以用一个搜索来解决的,但是我们要快,一个搜索是不够的
那么优化搜索的方法是什么?
记忆化!
对的,我们在这里有一个比较好玩的性质,对于每一个长度相同的区间,这两个区间中相对应的两个下标 i , j i,j i,j 到这两个下标的最深深度距离是一样的
可能有点抽象,我们举个栗子:
上图中第 2 2 2 层的 [ 1 , 3 ] [1,3] [1,3] 和 [ 4 , 6 ] [4,6] [4,6],区间长度一样
那么 [ 1 , 3 ] [1,3] [1,3]区间中的第 2 2 2 个数也就是 2 2 2,它到 [ 2 , 2 ] [2,2] [2,2]的距离是 2 2 2;
[ 4 , 6 ] [4,6] [4,6]区间中的第 2 2 2 个数也就是 5 5 5,它到 [ 5 , 5 ] [5,5] [5,5]的距离也是 2 2 2;
当然,我们多找几个来举例,也是一样的
也就是说我们是可以通过这种方法实现记忆化
void dfs(int l,int r,int sum){
if(mem[r-l+1]==true){
for(int i=l;i<=r;i++){
s[i]=s[i-l+s1[r-l+1]]-s2[r-l+1]+sum;
}
return;
}
if(l==r){
s[l]=sum;
w=max(w,sum);
return;
}
int mid=(l+r)>>1;
dfs(l,mid,sum+1);
dfs(mid+1,r,sum+1);
s1[r-l+1]=l;
s2[r-l+1]=sum;
mem[r-l+1]=true;
}
处理好了深度,剩下的事情就真的简单了,一个前缀和, o v e r over over
#include
#include
using namespace std;
const int MAXN=1000005;
int s1[MAXN];
int s2[MAXN];
long long s[MAXN];
bool mem[MAXN];
long long pre[MAXN];
int dep[MAXN],pre_d[MAXN];
int w;
void dfs(int l,int r,int sum){
if(mem[r-l+1]==true){
for(int i=l;i<=r;i++){
s[i]=s[i-l+s1[r-l+1]]-s2[r-l+1]+sum;
}
return;
}
if(l==r){
s[l]=sum;
w=max(w,sum);
return;
}
int mid=(l+r)>>1;
dfs(l,mid,sum+1);
dfs(mid+1,r,sum+1);
s1[r-l+1]=l;
s2[r-l+1]=sum;
mem[r-l+1]=true;
}
int main(){
int n,m;
long long qwq;
scanf("%d %d %lld",&n,&m,&qwq);
qwq*=128;
dfs(1,n,1);
dep[1]=qwq;
pre_d[1]=qwq;
for(int i=2;i<=w;i++){
dep[i]=dep[i-1]>>1;
pre_d[i]=pre_d[i-1]+dep[i];
}
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+pre_d[s[i]];
}
// for(int i=1;i<=n;i++){
// cout<
// }
long long ans=0;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
ans+=(pre[i]-pre[i-1])*x;
}
for(int i=1;i<=m;i++){
int l,r,x;
scanf("%d %d %d",&l,&r,&x);
ans+=(pre[r]-pre[l-1])*x;
printf("%lld\n",ans/128);
}
return 0;
}