P3924 康娜的线段树(期望)

Posted kafuuchino

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3924 康娜的线段树(期望)相关的知识,希望对你有一定的参考价值。

P3924 康娜的线段树

看起来$O(nlogn)$可过其实由于巨大常数是无法通过的

$O(nlogn)$:70pts

我们手玩样例发现

线段树上某个节点的期望值$f[o]=(f[lc]+f[rc])/2+sum[o]$

$s[o]$表示该节点代表的区间和。

每次$Add(l,r,x)$时,每个x对于$f[o]$的贡献是固定的,即$f[o]+=x*k[o]$

这个$k[o]$可以在建树时预处理。

然鹅卡不过TAT

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef double db;
typedef long long ll;
template <typename T> void read(T &x){
    static char c=getchar();x=0; bool f=1;
    while(c<0||c>9) f=f&&(c!=-),c=getchar();
    while(0<=c&&c<=9) x=x*10+(c^48),c=getchar();
    x=f?x:-x;
}
#define N 4000005
int n,m,qwq; db f[N],k[N]; ll s[N],add[N];
#define lc o<<1
#define rc o<<1|1
#define mid (l+r)/2
inline void up(int o){s[o]=s[lc]+s[rc],f[o]=(f[lc]+f[rc])/2.0+s[o];}
void down(int o,int l,int r){
    if(add[o]==0) return ;
    s[lc]+=1ll*(mid-l+1)*add[o]; s[rc]+=1ll*(r-mid)*add[o];
    f[lc]+=k[lc]*add[o]; f[rc]+=k[rc]*add[o];
    add[lc]+=add[o]; add[rc]+=add[o]; add[o]=0;
}
void build(int o,int l,int r){
    if(l==r){read(s[o]); f[o]=s[o]; k[o]=1.0; return ;}
    build(lc,l,mid); build(rc,mid+1,r);
    k[o]=+(k[lc]+k[rc])/2.0+(db)(r-l+1); up(o);
}
void Add(int o,int l,int r,int x1,int x2,int v){
    if(x1<=l&&r<=x2){
        s[o]+=1ll*(r-l+1)*v; f[o]+=k[o]*(db)v; add[o]+=v;
        return ;
    }down(o,l,r);
    if(x1<=mid) Add(lc,l,mid,x1,x2,v);
    if(x2>mid) Add(rc,mid+1,r,x1,x2,v);
    up(o);
}
int main(){
    read(n);read(m);read(qwq); int q1,q2,q3;
    build(1,1,n);
    while(m--){
        read(q1);read(q2);read(q3);
        Add(1,1,n,q1,q2,q3);
        printf("%.0lf\n",f[1]*(db)qwq);
    }return 0;
}

$O(n)$:100pts

我们直接看每个叶节点对答案的贡献

贡献$=$概率$*$从根节点到该叶节点上的各点权值和

概率在建树时即可预处理,而权值和在询问时可以顺便处理掉

每次$Add(l,r,x)$时,考虑每个$x$对答案的贡献

$x \times \sum_{i=1}^{dep}{\frac{1}{2^{i-1}}}$

后面的东西是等比数列,可以化成$\frac{2^{dep}-1}{2^{dep-1}}$

区间修改的话就维护这个东西的前缀和

于是我们算出所有期望和再直接除以$2^{maxd}$就好辣

注意$2^{maxd}$与$qwq$需要约分,防爆精度

#include<iostream>
#include<cstdio>
#include<cstring>
typedef double db;
typedef long long ll;
inline int Max(int a,int b){return a>b?a:b;}
template <typename T> void read(T &x){
    static char c=getchar();x=0; bool f=1;
    while(c<0||c>9) f=f&&(c!=-),c=getchar();
    while(0<=c&&c<=9) x=x*10+(c^48),c=getchar();
    x=f?x:-x;
}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
#define N 1000005
int n,m,Md; ll sum[N<<2],s[N],ans,y,qwq,d[N];
#define lc o<<1
#define rc o<<1|1
#define mid (l+r)/2
void build(int o,int l,int r,int D){
    if(l==r){
        read(sum[o]); d[l]=D; Md=Max(Md,D); 
        return ;
    }build(lc,l,mid,D+1); build(rc,mid+1,r,D+1);
    sum[o]=sum[lc]+sum[rc];
}
ll Ask(int o,int l,int r,int t,ll tt){
    if(l==r) return 1ll*(1ll<<t)*(tt+sum[o]);
    return Ask(lc,l,mid,t-1,tt+sum[o])+
    Ask(rc,mid+1,r,t-1,tt+sum[o]);
}
int main(){
    read(n);read(m);read(qwq); int q1,q2,q3;
    build(1,1,n,1);
    ans=Ask(1,1,n,Md-1,0); y=1ll<<(Md-1);
    ll g=gcd(qwq,y); qwq/=g; y/=g;
    for(int i=1;i<=n;++i)
        s[i]=s[i-1]+1ll*((1ll<<d[i])-1)*(1ll<<(Md-d[i]));
    while(m--){
        read(q1);read(q2);read(q3);
        ans+=1ll*(s[q2]-s[q1-1])*q3;
        printf("%lld\n",ans/y*qwq);
    }return 0;
}

 

以上是关于P3924 康娜的线段树(期望)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P3924 康娜的线段树

JZYZOJ1527 [haoi2012]高速公路 线段树 期望

Omeed 线段树

神奇脑洞题解——HAOI2012高速公路(数学期望,线段树)

Bzoj 2752 高速公路 (期望,线段树)

[CF895E] Eyes Closed(线段树,期望)