191108单调队列

谨以此博记单调队列里的一些毒瘤算法

而且搞了半天才发现单调队列原来是具有单调性的队列的意思
还有人用双端队列实现(又是一个不懂的知识点)

所以单调队列就是用单调性解决问题
常用的操作就是逆单调性的元素删去,每次删去时要求有二——维持队列l<=r,满足单调性
需要的数组有:原数组,记录单调队列元素的序号,单调队列本身

T1 Select

spojP392
原来想的是用一个单调队列模拟
但是发现时间肯定要炸
而且每次打模拟的时候都要出一点细节问题/ll/ll/ll

然后看了这个喵喵喵的题解:
这道题中,我们要的只是最大的一个元素,并且要排在前面
所以排在这个元素之前的不优于它的就没有存在的必要了
可以删掉
因为当前加入的元素比原来的队尾元素大,且该元素比原队尾元素入队晚(也就是该元 素比原队尾元素晚出队),所以只要该元素在队列中,就一定不会选取原队尾元素。也就是 当前状态一定比原队尾元素的状态更优。——这里一定要理解深刻,这是队列的本质。 ——zgs
因此,这题的单调队列中维护的一个属性是元素的价值,一个属性是单调队列中的元素在原序列中的位置。

#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
struct node{
	int key,id;
};
deque<node> q;
int T;char ch[233];int id;
int key;
int last;
void print(int n)
{
	if(n>9) print(n/10);
	putchar(n%10+48);
}
char chh;
signed main(){
	T=in;
	while(T--){
		q.clear();
		id=0;
		scanf("%s",ch);last=0;
		while(1){
			chh=getchar();
			if(chh=='E')break;
			if(chh=='C'){
				key=in;++id;
				node now={key,id};
				while(q.size()&&now.key>=q.back().key)q.pop_back();
				q.push_back(now);
			}
			else if(chh=='Q'){
				if(q.empty())puts("-1");else print(q.front().key),putchar('\n');
			}
			else if(chh=='G'){
				++last;if(q.front().id==last)q.pop_front();
			}
		}
	}
	return 0;
} 

数据太坑,必须优化
然鹅优化之后还是要T
总之前三个点对了就是了
然后抄了个gigo的代码

T2 Board

spojP393
对于每一个楼房,它的广告牌最多这样算:
S i = h i × ( r i l i 1 ) \quad \qquad \qquad S_i=h_i \times (r_i-l_i-1)
因此求出它的l和r(左右的最大端点)
与其说这种求法是单调队列,不如说它更像一个栈——
当高度单调递增时,高度放入栈
当有一个高度没有单增时,比此高更高的高度弹出,其左端点或右端点为此高度的编号

sol

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define ll long long
#define NNN 1000005
int n,h[NNN];
int lft[NNN],rgh[NNN];
int p[NNN],r,l;

inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

int main(){
	n=in;
	for(re int i=1;i<=n;i++)h[i]=in;
	h[0]=h[n+1]=0;
	
	r=0,l=1;
	for(re int i=1;i<=n+1;i++){
		while(h[i]<h[p[r]]&&l<=r){
			rgh[p[r]]=i;
			r--;
		}
		p[++r]=i;
	}
	
	r=0,l=1;
	memset(p,0,sizeof(p));
	for(re int i=n;i>=0;i--){
		while(h[i]<h[p[r]]&&l<=r){
			lft[p[r]]=i;
			r--;
		}
		p[++r]=i;
	}
	
	ll ans=0;
	for(re int i=1;i<=n;i++)
		ans=max(ans,(ll)h[i]*(rgh[i]-lft[i]-1));
	
	printf("%lld",ans);
	return 0;
}

又是一个long long ans输出%d ahhhhhhhhh /fn/fn/fn

T3 Window

spojP1917
很典型的单调队列题
先使左端点靠近,再使右端点靠近,最后往指针数组里面加数,记录,完了

通过这道题我明白了为什么我看不懂zgs的代码了
1 他根本没讲单调数组的定义
2 他的代码喜欢用p1,p2之类的东西,你不知道1或者2代表什么
3 还是对这个知识点不熟

下面给出我的好看的sol


#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
const int NNN=(int)1e6+10;
int n,k,a[NNN];
int p_max[NNN],p_min[NNN];
int l_max,r_max,l_min,r_min;
int ans_max[NNN],ans_min[NNN];

inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

//致那些年兴起的输出优化 
inline void Write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)Write(x/10);
	putchar(x%10+48);
}

int main(){
	n=in,k=in;
	for(re int i=1;i<=n;i++)a[i]=in;
	
	for(re int i=1;i<=n;i++){
		//处理_max
		while(l_max<=r_max&&p_max[l_max]<=i-k)l_max++;//左指针调到队尾 
		while(l_max<=r_max&&a[i]>a[p_max[r_max]])r_max--;
		p_max[++r_max]=i;
		//处理_min
		while(l_min<=r_min&&p_min[l_min]<=i-k)l_min++;
		while(l_min<=r_min&&a[i]<a[p_min[r_min]])r_min--;
		p_min[++r_min]=i;
		//记录该点的答案 
		//l是移好的最值 
		ans_max[i]=a[p_max[l_max]];
		ans_min[i]=a[p_min[l_min]];
	}
	
	for(re int i=k;i<=n;i++)Write(ans_min[i]),putchar(' ');
	putchar('\0');
	for(re int i=k;i<=n;i++)Write(ans_max[i]),putchar(' ');
	
	return 0;
}

T4 List

单调队列
维护前缀和单调递增
要求条件:
1 前缀和单调递增
2 下标单调递增
3 区间长度 l e n m len\leq m
附上最好看的单调队列分析图
分析应该> 还是<
在这里插入图片描述
在这里插入图片描述
附上最妙的代码

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define ll long long
const int NNN=(int)3e5+10;
int n,m;
ll sum[NNN],x,ans;
ll p[NNN],l,r;

inline ll in{
	ll i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

int main(){
	n=in;
	m=in;
	for(re int i=1;i<=n;i++){
		x=in;
		sum[i]=sum[i-1]+x;
	}
	
	l=1,r=1;
	for(re int i=1;i<=n;i++){
		while(l<=r&&p[l]<=i-m)l++;
		ans=max(ans,sum[i]-sum[p[l]]);
		while(l<=r&&sum[i]<=sum[p[r]])r--;
		p[++r]=i;
	}
	
	printf("%lld\n",ans);
	return 0;
}
发布了26 篇原创文章 · 获赞 3 · 访问量 909

猜你喜欢

转载自blog.csdn.net/Antimonysbguy/article/details/102976280