zoukankan      html  css  js  c++  java
  • 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
    ",f[1]*(db)qwq);
        }return 0;
    }

    $O(n)$:100pts

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

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

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

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

    $x imes 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
    ",ans/y*qwq);
        }return 0;
    }
  • 相关阅读:
    java中判断图片格式并且等比例压缩图片
    如何将freemarker文件转化为html文件
    细数用anaconda安装mayavi时出现的各种问题
    利用java代码生成keyStore
    时间戳获取 天/月/日等until
    navicat连接msql Client does not support authentication protocol requested by server; consider upgrading MySQL client
    类初始化和构造器初始化的区别
    git从自己账号切换到公司的账号,剪项目失败
    求吸血鬼数(1000~10000,thinking)
    vue。js的时间的格式化
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10624882.html
Copyright © 2011-2022 走看看