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;
}

原文地址:https://www.cnblogs.com/kafuuchino/p/10624882.html

时间: 2024-07-31 00:12:59

P3924 康娜的线段树(期望)的相关文章

洛谷 P3924 康娜的线段树

P3924 康娜的线段树 题目描述 小林是个程序媛,不可避免地康娜对这种人类的“魔法”产生了浓厚的兴趣,于是小林开始教她OI. 今天康娜学习了一种叫做线段树的神奇魔法,这种魔法可以维护一段区间的信息,是非常厉害的东西.康娜试着写了一棵维护区间和的线段树.由于她不会打标记,因此所有的区间加操作她都是暴力修改的.具体的代码如下: struct Segment_Tree{ #define lson (o<<1) #define rson (o<<1|1) int sumv[N<&l

P3924 康娜的线段树

上面两位dalao谈笑风生就讲完了这道题,但身为蒟蒻的我一窍不通,好不容易想通了写个题解纪念一下. 这道题要维护一个线段树里面的期望.期望是这么计算的: 如果每次在线段树区间加操作做完后,从根节点开始等概率的选择一个子节点进入,直到进入叶子结点为止,将一路经过的节点权值累加,最后能得到的期望值是多少? 最朴素计算期望的思路 根结点权值乘以1 + 所有第二层结点权值和乘以二分之一 + 所有第三层结点权值和乘以四分之一 直到到达叶子结点为止,把这些东西都加起来就是答案. 一个优化的思路 因为从根节点

BZOJ 2752: [HAOI2012]高速公路(road) [线段树 期望]

2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1219  Solved: 446[Submit][Status][Discuss] Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1

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

http://172.20.6.3/Problem_Show.asp?id=1527 日常线段树的pushdown写挂,果然每次写都想得不全面,以后要注意啊--求期望部分也不熟练,和平均数搞混也是orz,我已经是个期望都求不出来的废人了.这道题显然(大概)每个段的贡献是val[i]*(y-i+1)*(i-x+1);整体来说算是一看就是线段树的题. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring>

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

Desctiption 传送门:Portal 大致题意: 给你一个序列, 支持两种操作: 1 l1 r1 l2 y2 在\([l1, r1]\)随机选择一个数a, \([l2, r2]\) 内随机选择一个数b, 交换a, b. 2 l r 询问一个区间的期望. \[ n \leq 200000; a_i \leq 1e9 \] Solution 根据==期望线性性==,只需要维护出==每个位置元素值的期望==就可以. ==区间操作期望==问题的经典套路是==维护出每个位置的期望==. 考虑一个数

BZOJ 4262 线段树+期望

思路: 把询问离线下来,查询max和查询min相似,现在只考虑查询max 令sum[l,r,x]表示l到r内的数为左端点,x为右端点的区间询问的答案 那么询问就是sun[l1,r1,r2]-sum[l1,r1,l1-1] 从1到n枚举x,维护区间线段树表示sum[l,r,x],发现从x-1转移到x的过程中,每个数加上了max(a[pos]..a[x])的答案. 用单调队列维护一个单调递减的序列,由于a数列是随机的,这个队列期望有log个元素,所以只需要对这log段暴力修改,复杂度nlog^2n

hdu-5805 NanoApe Loves Sequence(线段树+概率期望)

题目链接: NanoApe Loves Sequence Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 262144/131072 K (Java/Others) Problem Description NanoApe, the Retired Dog, has returned back to prepare for the National Higher Education Entrance Examination! In

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

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

[JSOI2008][BZOJ1012] 最大数(动态开点线段树)

题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前数列的长度. 2. 插入操作. 语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾. 限制:n是整数(可能为负数)并且在长整范围内. 注意:初始时数列是空的,没有一个数. 输入输出格式 输入格式: 第一行两个整数,M