P3924 康娜的线段树(期望)
Posted kafuuchino
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 康娜的线段树(期望)的主要内容,如果未能解决你的问题,请参考以下文章
JZYZOJ1527 [haoi2012]高速公路 线段树 期望