1 条题解

  • 0
    @ 2025-8-24 21:59:42

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 田大坑
    **

    搬运于2025-08-24 21:59:42,当前版本为作者最后更新于2018-11-03 07:37:27,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    超级树状数组


    对于大部分oier来说,码线段树是一件十分困难的事情,对于这一个OB的算法又不得不去学习

    而对于另一种友好的树形结构——树状数组就特别好码,可惜的是只能去求区间的和,以及单点修改

    真的是这样吗?树状数组不是什么鸡肋,其实可以和线段树有一样的操作,对于区间的最大最小,是可以手动维护的

    void add(int p,int x)//添加一个数(不能修改)
    {
        for(;p<=n;p+=p&(-p))
        {
            c[p].mx=max(c[p].mx,x);//维护区间的最大以及最小
            c[p].mn=min(c[p].mn,x);
        }
    }
    

    之后求和就好了

    bool sum(int l,int r)
    {
        int mx=-0x7f7f7f7f,mn=0x7f7f7f7f;
        while(l<=r)
        {
            for(;r-(r&(-r))>=l;r-=r&(-r))//标配
            {
                mx=max(c[r].mx,mx);
                mn=min(c[r].mn,mn);
            }
            mx=max(mx,a[r]);
            mn=min(mn,a[r]);
            r--;//不是所有区间都已处理,所以会多次维护
        }
        if(abs(mx-mn)<=ci)
        return true;
        return false;
    }
    

    a下这到题目只需要上述两个操作

    下面ac代码

    #include<bits/stdc++.h>
    using namespace std;
    struct tree{
        int mx=-0x7f7f7f7f,mn=0x7f7f7f7f,a;
    }c[1100101];
    int n,m,ci,ans[1000001],a[1000001],cnt;
    void add(int p,int x)
    {
        for(;p<=n;p+=p&(-p))
        {
            c[p].mx=max(c[p].mx,x);
            c[p].mn=min(c[p].mn,x);
        }
    }
    bool sum(int l,int r)
    {
        int mx=-0x7f7f7f7f,mn=0x7f7f7f7f;
        while(l<=r)
        {
            for(;r-(r&(-r))>=l;r-=r&(-r))
            {
                mx=max(c[r].mx,mx);
                mn=min(c[r].mn,mn);
            }
            mx=max(mx,a[r]);
            mn=min(mn,a[r]);
            r--;
        }
        if(abs(mx-mn)<=ci)
        return true;
        return false;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        scanf("%d%d%d",&n,&m,&ci);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&a[i]);
            add(i,a[i]);
        }
        for(int i=1;i<=n-m+1;i++)
        {
            if(sum(i,i+m-1))
            ans[++cnt]=i;
        }
        for(int i=1;i<=cnt;i++)
        {
            printf("%d\n",ans[i]);
        }
        if(cnt==0)
        cout<<"NONE";
    }
    

    其实怎么做的原因,手动模拟一下树状数组的维护操作就可以知道

    其实我们还可以进行区间修改以及查询

    void add(int c[],int p,int x)
    {
        while(p<=n)
        {
            c[p]+=x;
            p+=p&(-p);
        }
    }
    void modify(int l,int r,int x)//区间修改(l——r的每一个数加上x)
    {
        add(d1,l,x);
        add(d1,r+1,-x);
        add(d2,l,x*(l-1));
        add(d2,r+1,-x*r);
    }
    int sum_(int c[],int p)
    {
        int s=0;
        while(p)
        {
            s+=c[p];
            p-=p&(-p);
        }
        return s;
    }
    int query(int l,int r)//区间求和(l——r)
    {
        return r*sum_(d1,r)-sum_(d2,r)-((l-1)*sum_(d1,l-1)-sum_(d2,l-1));//想一想,为什么(huaji)
    }
    

    那么我们就用简单易懂的树状数组代替了线段树,由于代码量少,认真思考一两分钟就可以得出原因的代码,就不多加阐述

    在考场上,面对功能极度全面的线段树O(Ologn),以及代码量少的树状数组O(nlognlogn),加以权衡

    其实想要详细解释,可以去看洛谷日报22期,有说明和图的

    • 1

    信息

    ID
    3340
    时间
    1000ms
    内存
    125MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者