堆与ST表

堆分为插入,查询,删除3个操作,虽然我们通常用优先队列来实现堆的功能,但是手写堆还是需要会的。
P3378 【模板】堆
最基本的二叉堆,也就是用一个二叉树来模拟的,其中通过一定的单调性来排列,其中堆顶为单调性最高的点位。
类似于现端倪树一般的,我们父节点的左儿子记为now<<1,右儿子为(now<<1)+1

1.插入

插入的时候,我们把该节点放在堆底,每次与它的父节点比较,看是否需要交换即可。

点击查看代码
heap[++siz]=x;
now=siz;
while(now) {
	int nxt=now>>1;
	if(heap[nxt]>heap[now]) {
		swap(heap[nxt],heap[now]);
		now=nxt;
	}else {
		break;
	}
}

2.查询

这个比较简单,直接返回堆顶即可。
return heap[1];

3.删除

对于删除堆顶的需求,我们删除了堆顶,必然就需要补全,可以挑选堆底的一个元素,放在堆顶,与它的左右儿子进行比较,然后判断是否交换。

点击查看代码
swap(heap[siz],heap[1]);
--siz;
now=1;
while((now<<1)<=siz) {
	int nxt=now<<1;
	if(nxt+1<=siz&&heap[nxt+1]<heap[nxt])
		++nxt;
	if(heap[nxt]<heap[now]) {
		swap(heap[nxt],heap[now]);
		now=nxt;
	}else {
		break;
	}
}

ST表

P3865 【模板】ST 表
ST表一般用于求静态区间的最值,其对于长度为\(n\)的数列的初始化复杂度为\(O(NlogN)\),查询复杂度为\(O(1)\)
我们设一个一维数组\(lg[n]\)和一个二维数组\(f[n][lg[n]]\),其中\(lg[n]\)存储的是为\(n\)取对数并向下取整得到的值,\(f[i][j]\)存的是第\(i\)个数,从它到它后面\(\large 2^j-1\)个数中的最大值。
初始化的方法就已经很显然了,我们从更小的区间来合并即可,\(\large f[i][j]=cal(f[i][j-1],f[i+2^{j-1}][j-1])\)
\(\large [i,i+2^j-1]\)\(\large [i,i+2^{j-1}-1]\)\(\large [i+2^{j-1},i+2^{j}-1]\)合并出答案.
以区间最大值为例,对应代码:

点击查看代码
    lg[0]=-1;
    for(i=1;i<=n;i++) {
        cin>>f[i][0];
        lg[i]=lg[i>>1]+1;
        // cout<<i<<" "<<lg[i]<<endl;
    }
    for(int j=1;j<=lg[n];j++) {
        for(i=1;i<=n-(1<<j)+1;i++) {
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
而在查询过程中,我们可以考虑用两个重叠区间来解决这个问题,比如给定的任意的$[L,R]$,我们可以用其中的两个小区间来查询,考虑到与2的幂次有关,我们可以取$k=lg[R-L+1]$,则$[L,L+2^k-1]$和$[R-2^k+1,R]$必定覆盖了所求的$[L,R]$区间。 以最大值为例,代码为:
点击查看代码
	cin>>L>>R;
	int k=lg[R-L+1];
	cout<<max(f[L][k],f[R-(1<<k)+1][k])<<endl;
posted @ 2024-04-23 16:17  WE-R  阅读(17)  评论(0)    收藏  举报