基础数据结构

队列

取出放入问题

给一个序列,将最小值x取出,再将f(x)放入

一般f(x)符合某些规律或性质,多开若干个序列维护即可

例1 [P2827] 蚯蚓

例2 [P6033] 合并果子 加强版

例1 P1631序列合并

有两个长度都为n的序列A、B,在A和B中各选一个数相加可以得到n2个数。求这n2个数中最小的n个。

找单调性

A1+B1<=A1+B2<=A1+B3<=…<=A1+Bn

A2+B1<=A2+B2<=A2+B3<=…<=A2+Bn

......

An+B1<=An+B2<=An+B3<=…<=An+Bn

每个序列的队首元素一定为该序列的最小值。

将每个序列的最小元素丢进堆中,删除队首,每取出一个,则加入该序列新的队首

时间复杂度O(nlogn)

对顶堆

提起O(logn)求第k大数,一般想起用平衡树

然而当只需维护加点、询问操作,且询问有序时,可以用对顶堆

可用于顺序求解第k大数

例1 P1801 黑匣子

维护两个操作

1,ADD(x) 将x加入集合中
2,GET(i)i加1,并输出该集合中第i小的数

维护一个小根堆,一个大根堆,且保证小根堆的堆顶元素大于大根堆的堆顶元素

若此时大根堆中有x个元素,则整个集合中第x大的元素为大根堆堆顶

问题转换为维护两个堆(对顶堆)

例2 求全部子区间的中位数

对每个起点做一遍“黑匣子”即可,时间复杂度O(n2logn)

这题还有O(n2)解法,可以自己试试推

P7915 [CSP-S 2021] 回文

P1155 [NOIP2008 提高组] 双栈排序

单调栈

求每个数左面第一个小于等于他的下标位置。

例1

已知 f(l,r)=maxi=lrimini=lri

l=1nr=lnf(l,r) ,其中 1n106

#include<iostream>
using namespace std;
const int MAXN=1000000+100;
int n,a[MAXN],ma[MAXN],mi[MAXN],t1=0,t2=0;
long long ans=0;
int main() {
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) {
		//for(int j=1;j<=t1;j++) cout<<ma[j]<<' ';cout<<endl;
		//for(int j=1;j<=t2;j++) cout<<mi[j]<<' ';cout<<endl;
		while(t1-1>0&&a[ma[t1]]<a[i]) {
			ans+=1ll*a[ma[t1]]*(ma[t1]-ma[t1-1])*(i-ma[t1]);
			t1--;
		}
		while(t2-1>0&&a[mi[t2]]>a[i]) {
			ans-=1ll*a[mi[t2]]*(mi[t2]-mi[t2-1])*(i-mi[t2]);
			t2--;
		}
		if(t1==1&&a[ma[t1]]<a[i]) ans+=1ll*a[ma[t1]]*ma[t1]*(i-ma[t1]),t1--;
		if(t2==1&&a[mi[t2]]>a[i]) ans-=1ll*a[mi[t2]]*mi[t2]*(i-mi[t2]),t2--;
		ma[++t1]=i;
		mi[++t2]=i;
	}
	for(;t1-1>0;t1--) ans+=1ll*a[ma[t1]]*(ma[t1]-ma[t1-1])*(n-ma[t1]+1);
	for(;t2-1>0;t2--) ans-=1ll*a[mi[t2]]*(mi[t2]-mi[t2-1])*(n-mi[t2]+1);
	ans+=1ll*a[ma[t1]]*ma[t1]*(n-ma[t1]+1)-1ll*a[mi[t2]]*mi[t2]*(n-mi[t2]+1);
	cout<<ans<<endl;
	return 0;
}

例2

给定一个1~n的排列A,对于这个排列定义一个函数f(l,r)=max(Al,Al+1..., Ar)请你求出对于所有1≤l≤r≤n,前K大的f(l,r)的和。

注意到,以一个数为最大值的区间,左端点一定在这个数往左最近的一个比它大的数的右边,右端点一定在这个数往右最近的一个比它大的数的左边。因为所给的数列是一个排列,所以不会有相同元素,所以我们对于每一个元素,找到它左边最近的比它大的数的位置i,找到它右边最近的比它大的数的位置jj,设这个数本身位置为k,那么显然以这个数为最大值的区间数量为(k−i)(j−k)个。

单调栈,寻找出每个元素左右第一个比他大的数。

时间复杂度O(n)

posted @   Zhone_lb  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示