首页 > 其他分享 >线段树维护区间方差

线段树维护区间方差

时间:2024-08-09 20:27:41浏览次数:11  
标签:方差 int double 线段 rs tag ls 区间 sum

线段树维护区间方差

方差

区间方差

还教室

  • 解题思路:
    利用线段树维护 \(a_i\) 与 \(a_i^2\) \(( 1\leq i \leq n)\) 两个数列 ,然后使用一个 \(lazytag\) 来记录是否进行了区间加,最后输出方差的时候使用 $$ s^2 =\sum\limits_{i=1}^n (a_i-\overline a )^2 =(\sum \limits_{i=1}na_i2+n\times\overline a^2-2 n\overline a$$ 就可以计算出区间方差
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 9;
#define int long long
int n, m;
class seg
{
public:
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)

    struct node
    {
        int l, r;
        double tag;
        double sum, fsum; // 算术和与平方和
    };
    node t[maxn<<2];
    double a[maxn];
    void build(int p, int l, int r)
    {
        t[p].l = l, t[p].r = r;
        if (l == r)
        {
            t[p].sum = a[l];
            t[p].fsum = t[p].sum * t[p].sum;
            return;
        }
        int mid = l + r >> 1;
        build(ls(p), l, mid);
        build(rs(p), mid + 1, r);
        t[p].sum = t[ls(p)].sum + t[rs(p)].sum;
        t[p].fsum = t[ls(p)].fsum + t[rs(p)].fsum;
    }
    void pushdown(int p)
    {
        if (t[p].tag)
        {
            if(t[p].l!=t[p].r)
            {
                t[ls(p)].fsum+=(t[ls(p)].r-t[ls(p)].l+1)*t[p].tag*t[p].tag+2*t[p].tag*t[ls(p)].sum;
                t[rs(p)].fsum+=(t[rs(p)].r-t[rs(p)].l+1)*t[p].tag*t[p].tag+2*t[p].tag*t[rs(p)].sum;
                t[ls(p)].tag+=t[p].tag;
                t[rs(p)].tag+=t[p].tag;
                t[rs(p)].sum += (t[rs(p)].r - t[rs(p)].l + 1) * t[p].tag;
                t[ls(p)].sum += (t[ls(p)].r - t[ls(p)].l + 1) * t[p].tag;
                t[p].tag=0;
            }
            else
            {
                t[p].tag=0;
            }
        }
        
    }
    void change(int p, int l, int r,double x)
    {
        if(l<=t[p].l&&t[p].r<=r)
        {
            t[p].fsum+=(t[p].r-t[p].l+1)*x*x+2*x*t[p].sum;
            t[p].sum+=x*(t[p].r-t[p].l+1);
            t[p].tag+=x;
            return ;
        }
        if(t[p].r<l||t[p].l>r)return ;
        pushdown(p);
        int mid=t[p].l+t[p].r>>1;
        if(mid>=l)change(ls(p),l,r,x);
        if(mid<r) change(rs(p),l,r,x);
        t[p].sum = t[ls(p)].sum + t[rs(p)].sum;
        t[p].fsum = t[ls(p)].fsum + t[rs(p)].fsum;
    }
    double asksum(int p,int l,int r)
    {
        if(l<=t[p].l&&t[p].r<=r)
        {
            return t[p].sum;
        }
        if(t[p].r<l||t[p].l>r)return 0;
        double ans=0;
        pushdown(p);
        int mid=t[p].l+t[p].r>>1;
        if(mid>=l)ans+=asksum(ls(p),l,r);
        if(mid<r) ans+=asksum(rs(p),l,r);
        return ans;
    }
    double askfsum(int p,int l,int r)
    {
        if(l<=t[p].l&&t[p].r<=r)
        {
            return t[p].fsum;
        }
        if(t[p].r<l||t[p].l>r)return 0;
        double ans=0;
        pushdown(p);
        int mid=t[p].l+t[p].r>>1;
        if(mid>=l)ans+=askfsum(ls(p),l,r);
        if(mid<r) ans+=askfsum(rs(p),l,r);
        return ans;
    }
};
seg T;
signed main()
{
    std::ios::sync_with_stdio(0);
    std::cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> T.a[i];
    }
    T.build(1,1,n);
    int t,x,y;
    double k;
    for(int i=1;i<=m;i++)
    {
        std::cin>>t;
        if(t==1)
        {
            cin>>x>>y;cin>>k;T.change(1,x,y,k);
        }
        else if(t==2)
        {
            std::cin>>x>>y;
            printf("%.4f\n",(double)T.asksum(1,x,y)*1.0/(1.0*(y-x+1)));
        }
        else 
        {
            cin>>x>>y;
            double t1=(double)T.asksum(1,x,y)/(y-x+1);
            double t2=(double)T.askfsum(1,x,y);
            printf("%.4f\n",(double)(t2-t1*t1*(y-x+1))/(y-x+1));
        }
    }
    return 0;
}

标签:方差,int,double,线段,rs,tag,ls,区间,sum
From: https://www.cnblogs.com/cxjy0322/p/18351443

相关文章

  • 洛谷 P3870 开关之线段树板子
    洛谷P3870题解传送锚点摸鱼环节[TJOI2009]开关题目描述现有\(n\)盏灯排成一排,从左到右依次编号为:\(1\),\(2\),……,\(n\)。然后依次执行\(m\)项操作。操作分为两种:指定一个区间\([a,b]\),然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);指定一个区间......
  • 8.9 线段树板子+三分补题+三维的bfs
    nowcoder训练区间线段树板子题,我们只需要把区间每一个点设置成1,然后修改的时候直接改点,然后查区间就行线段树维护最大字段和/01串最大连续1的个数模板题。把白色和黑色看成1/0两个数就行了。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlon......
  • 【线段树合并/树上差分】[P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
    【线段树合并/树上差分】P4556[Vani有约会]雨天的尾巴/【模板】线段树合并思路对\(x,y,lca(u,v),fa_{lca(u,v)}\)四个点进行树上差分,然后用线段树合并动态权值线段树。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;template<classNode>str......
  • 线段树合并模板
    template<classNode>structPersidentSegmentTree{#definelc(u)tr[u].l#definerc(u)tr[u].rconstintn;inttot=0;vector<Node>tr;vector<int>root;PersidentSegmentTree():n(0){}PersidentSegmentTree(in......
  • 【题解】Solution Set - NOIP2024集训Day2 线段树
    【题解】SolutionSet-NOIP2024集训Day2线段树https://www.becoder.com.cn/contest/5431「CF1149C」TreeGenerator™结论:对于括号序列的一个子段,删去所有的匹配括号之后,剩下的不匹配的括号,按顺序构成树上的一条路径。Why?从括号序列的构造出发。每次(相当于开始遍历......
  • P3834 【模板】可持久化线段树 2
    P3834【模板】可持久化线段树2-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;template<classNode>structPersidentSegmentTree{#definelc(u)tr[u].l#definerc(u)tr[u].r#definesum(u)t......
  • 洛谷P3842 线段——题解
    洛谷P3842题解传送锚点摸鱼环节[TJOI2007]线段题目描述在一个\(n\timesn\)的平面上,在每一行中有一条线段,第\(i\)行的线段的左端点是\((i,L_{i})\),右端点是\((i,R_{i})\)。你从\((1,1)\)点出发,要求沿途走过所有的线段,最终到达\((n,n)\)点,且所走的路程长度要尽......
  • DAY6 位运算、离散化、区间和并
    本文所有题目都可在acwing题库中找到,本文仅进行归纳整理题目:acwing801给定一个长度为 n的数列,请你求出数列中每个数的二进制表示中 1的个数。输入格式第一行包含整数 n。第二行包含 n个整数,表示整个数列。输出格式共一行,包含 n个整数,其中的第 i个数表示数列中的第......
  • 数据结构——线段树优化 学习笔记
    数据结构——线段树优化学习笔记比较基础,因此讲的很快。我们主要关注单点修改、区间查询的线段树,这是应用最广泛的。线段树问题我们以LOJ的这道题为例,例题:LOJ#130.树状数组1:单点修改,区间查询。洛谷上面也有类似的题:P3374【模板】树状数组1。因为洛谷的题的数据范......
  • 数据结构——线段树提高 学习笔记
    数据结构——线段树提高学习笔记一些比较系统的东西,会单独放文章,这里只写一些理论的。线段树维护矩阵例题:P7453[THUSCH2017]大魔法师。当区间信息比较复杂,但是满足结合律的时候,可以使用矩阵维护。线段树每个节点维护一个矩阵,合并区间时使用矩阵乘法转移。但是,矩阵乘法的......