• 序列[势能线段树]


    也许更好的阅读体验

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

    • (a)([l,r])内所有的数加(1)
    • 询问(sumlimits_{i=l}^r lfloorfrac{a_i}{b_i} floor)

    (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 <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
    ",query(l,r));
    	}
    	return 0;
    }
    
    

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

  • 相关阅读:
    剑指Offer-30.连续子数组的最大和(C++/Java)
    剑指Offer-29.最小的K个数(C++/Java)
    UVA 1616 Caravan Robbers 商队抢劫者(二分)
    UVA 10570 Meeting with Aliens 外星人聚会
    UVA 11093 Just Finish it up 环形跑道 (贪心)
    UVA 12673 Erratic Expansion 奇怪的气球膨胀 (递推)
    UVA 10954 Add All 全部相加 (Huffman编码)
    UVA 714 Copying Books 抄书 (二分)
    UVALive 3523 Knights of the Round Table 圆桌骑士 (无向图点双连通分量)
    codeforecs Gym 100286B Blind Walk
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11715639.html
Copyright © 2020-2023  润新知