序列[势能线段树]

也许更好的阅读体验

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=lrbiai

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值