并查集
这道题目说的是有N件商品,每个商品有利润pi和过期时间di,每天只能卖一个商品,过期商品不能卖,求如何安排每天卖的商品,可以使收益最大。
这道题目有两种解法:
1)二叉堆解法
方法是初始建一个为空的小根堆,设定节点的值为商品的利润,分两种情况进行讨论:①商品数量大于堆顶,元素替换小根堆顶,②商品数量不足,将元素放入这个堆即可,最后输出结果。算法的时间复杂度是O(NlogN)STL 里priority_queue是一个大根堆的操作。
2)并查集解法
使用贪心策略,优先考虑卖出利润大的商品,将商品按照利润从大到小排序,并建立一个关于天数的并查集,每一天构成一个集和。对于每一个商品,若它在d天之后过期,就在并查集中查询d的树根(记为r)。若r大于0,则把该商品安排在r天卖出,合并r和r-1,累加到最后就是该商品的利润。整体上维护一个数组中“位置”的占用情况,利用并查集的路径压缩可以快速找到最晚能卖出的时间,即从过期时间往前数第一个空闲的天数。
树状数组
这里预备知识要知道 lowbit(R),其基本用途是维护序列的前缀和。对于给定的序列a,我们建立一个数组c,其中c【x】保存序列a的区间【x-lowbit(x)+1,x】,即可以看成如下的树形结构。
树状数组支持两种基本操作,第一个操作是查询前缀和,当要计算【l,r】中所有数的和,只需要计算ask(r)-ask(l-1);
第二个操作是单点增加,给序列中的a【x】加上y,同时正确维护序列的前缀和。
这道题目的题意是有n头奶牛,已知每头奶牛的具体身高,现在这n头奶牛站成一列,已知第i头奶牛前面有Ai头比它低,求每头奶牛的身高。
我们倒序来看这道题目,如果最后一头奶牛前面有An头比他低,那么它的身高显然是Hn=An+1
如果倒数第二头奶牛的前面有An-1头牛比它低,那么:
1)若An-1<An,则它的身高Hn-1=An-1 + 1
2)若An-1≥An,则它的身高是Hn-1=An-1 + 2
按照这样逻辑进行递推,如果第k头牛前面有Ak头比它低,那么它的身高Hk是数值1~n中第Ak+1小的没有在{Hk+1,Hk+2,...,Hk}中出现的数。
具体来说设置一个01的序列b,起初全部都是1.然后从n到1倒序扫描每个Ai,对每个Ai执行以下两个操作
1)查询序列b中第Ai+1个1在什么位置,这个位置号就是第i头奶牛的身高Hi
2)把b[Hi]减1(从1变为0)
也就是说,维护一个01序列,支持查询第k+1的位置,以及修改序列中的数值
一共有两种方法进行这个操作:
1)树状数组+二分
用树状数组c维护01序列b的前缀和,在每次查询时继续二分答案,通过ask(mid)即可得到前mid个数中有多少个1,与k比较大小,即可确定二分的上下界变化。
2)树状数组+倍增
用树状数组c维护01序列b的前缀和,在每次查询时:
1.初始化两个变量ans=0和sum=0
2.从log(向下取整)到0倒序考虑每个整数p
且,且令,
3.最后,Hi=ans+1即为答案
线段树
线段树是一种基于分治思想的二叉树结构
根据gcd的原理,我们可以知道gcd(x,y)=gcd(x,x-y),并且可以进一步扩展到gcd(x,y-x,z-y)
构造一个新的数列B,其中B[i]=A[i]-A[i-1] ,用线段树维护序列B的最大公约数
Qlr 就等于求出gcd(A[l],ask(1,l+1,r))
Clrd,只有B[l]加了d,B[r+1]被剪掉了d,所以在维护B的线段树上只需要两次单点修改即可。
询问时需要数列A中的值,可以额外用一支持“区间增加、单点查询”的树状数组对A进行维护。