D
e
s
c
r
i
p
t
i
o
n
\mathcal{Description}
Description
两个长度为
n
n
n的序列,
a
a
a,
b
b
b,其中
a
a
a最开始是一个全
0
0
0序列,
b
b
b是一个排列
你要用数据结构维护这样的两个操作
- 将 a a a中 [ l , r ] [l,r] [l,r]内所有的数加 1 1 1
- 询问 ∑ i = l r ⌊ a i b i ⌋ \sum\limits_{i=l}^r \lfloor\frac{a_i}{b_i}\rfloor i=l∑r⌊biai⌋
n n n和询问数均不超过 1 0 5 10^5 105
S o l u t i o n \mathcal{Solution} Solution
机房某大佬告诉咱这是势能线段树?
对于每个
i
i
i,每被加
b
i
b_i
bi次就产生
1
1
1的贡献,考虑维护这个过程
用一颗线段树,维护每个点的最大值和最大值在那个子树
每个节点的初值设为
−
b
i
-b_i
−bi
支持一下区间加,当发现当前最大值为
0
0
0时,就将那个点找到并加
1
1
1的贡献,贡献可用同一颗线段树或者树状数组维护
然后将那个点
(
(
(设为
i
)
i)
i)重新赋值为
−
b
i
-b_i
−bi
最多产生
n
l
o
g
n
nlogn
nlogn次贡献,每次维护
l
o
g
n
logn
logn
总复杂度
n
l
o
g
2
n
nlog^2n
nlog2n
C o d e \mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月21日 星期一 18时59分47秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn = 100005;
const int maxt = 1000006;
//{{{cin
struct IO{
template<typename T>
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;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧