序列[势能线段树]

也许更好的阅读体验

\(\mathcal{Description}\)
两个长度为\(n\)的序列,\(a\),\(b\),其中\(a\)最开始是一个全\(0\)序列,\(b\)是一个排列
你要用数据结构维护这样的两个操作

  • \(a\)\([l,r]\)内所有的数加\(1\)
  • 询问\(\sum\limits_{i=l}^r \lfloor\frac{a_i}{b_i}\rfloor\)

\(n\)和询问数均不超过\(10^5\)

\(\mathcal{Solution}\)

机房某大佬告诉咱这是势能线段树?
对于每个\(i\),每被加\(b_i\)次就产生\(1\)的贡献,考虑维护这个过程
用一颗线段树,维护每个点的最大值和最大值在那个子树
每个节点的初值设为\(-b_i\)
支持一下区间加,当发现当前最大值为\(0\)时,就将那个点找到并加\(1\)的贡献,贡献可用同一颗线段树或者树状数组维护
然后将那个点\((\)设为\(i)\)重新赋值为\(-b_i\)
最多产生\(nlogn\)次贡献,每次维护\(logn\)
总复杂度\(nlog^2n\)

\(\mathcal{Code}\)

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月21日 星期一 18时59分47秒
*******************************/
#include 
#include 
#include 
using namespace std;
const int maxn = 100005;
const int maxt = 1000006;
//{{{cin
struct IO{
    template
    IO & operator>>(T&res){
        res=0;
        bool flag=false;
        char ch;
        while((ch=getchar())>'9'||ch<'0')   flag|=ch=='-';
        while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
        if (flag)   res=~res+1;
        return *this;
    }
}cin;
//}}}
int n,m;
int h[maxn];
namespace SegmentTree{
    //其实可以不包装
    int lt[maxt],rt[maxt],ans[maxt],val[maxt],loc[maxt],lazy[maxt];
    #define cl (k<<1)
    #define cr (k<<1|1)
    #define lm (lt[k]+rt[k])/2
    #define rm (lt[k]+rt[k])/2+1
    //{{{pushup
    void pushup (int k)
    {
        if (lt[k]==rt[k])   return;
        if (val[cl]>val[cr])    val[k]=val[loc[k]=cl];
        else    val[k]=val[loc[k]=cr];
        ans[k]=ans[cl]+ans[cr];
    }
    //}}}
    //{{{build
    void build (int l,int r,int k=1)
    {
        lt[k]=l,rt[k]=r;
        if (l==r)   return loc[k]=k,val[k]=-h[l],void();
        build(l,lm,cl),build(rm,r,cr);
        pushup(k);
    }
    //}}}
    //{{{pushdown
    void pushdown (int k)
    {
        val[cl]+=lazy[k],val[cr]+=lazy[k];
        lazy[cl]+=lazy[k],lazy[cr]+=lazy[k];
        lazy[k]=0;
    }
    //}}}
    //{{{update
    void update (int k)
    {
        if (lt[k]==rt[k])   return ++ans[k],val[k]=-h[lt[k]],void();
        if (lazy[k])    pushdown(k);
        update(loc[k]);
        pushup(k);
    }
    //}}}
    //{{{modify
    void modify (int l,int r,int k=1)
    {
        if (lt[k]>=l&&rt[k]<=r){
            ++val[k],++lazy[k];
            while (!val[k]) update(k);
            return ;
        }
        if (lazy[k])    pushdown(k);
        if (l<=lm)  modify(l,r,cl);
        if (r>=rm)  modify(l,r,cr);
        pushup(k);
    }
    //}}}
    //{{{query
    int query (int l,int r,int k=1)
    {
        if (lt[k]>=l&&rt[k]<=r) return ans[k];
        if (lazy[k])    pushdown(k);
        int res=0;
        if (l<=lm)  res+=query(l,r,cl);
        if (r>=rm)  res+=query(l,r,cr);
        return res;
    }
    //}}}
}
using namespace SegmentTree;
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;++i)  cin>>h[i];
    build(1,n);
    for (int i=1;i<=m;++i){
        int opt,l,r;
        cin>>opt>>l>>r;
        if (opt==1) modify(l,r);
        else    printf("%d\n",query(l,r));
    }
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

你可能感兴趣的:(序列[势能线段树])