栈和队列小刷

Posted Virtualtan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈和队列小刷相关的知识,希望对你有一定的参考价值。

栈和队列

bzoj1012// JSOI2008 最大数 maxnumber

详见博客:https://www.cnblogs.com/tyner/p/11267630.html

bzoj2086 [Poi2010]Blocks

https://www.luogu.org/problem/P3503

题意

给出 N 个正整数 a[1...N],再给出一个正整数 k,现在可以进行
如下操作:
每次选择一个大于 k 的正整数 a[i],将 a[i] 减去 1,选择a[i - 1] 或 a[i + 1] 中的一个加上 1。
经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于 k。
总共给出 M 次询问,每次询问给出的 k 不同,你需要分别回答。
(N <= 1000000)(M <= 50)(k <= \\(10^9\\))
保证答案都在 long long 以内

分析

首先要明确一下,这里的M<=50,说明它需要O(n)的算法(不然它不就只让你修改一次,然后O(nlogn)水掉嘛....)(老师分析的,自己没往这儿想....以后注意一下吧)

其次:要求子序列的每个数都>=k,结合题目中的操作,即需要子序列的平均数>=k。(只要它的平均数>=k, 然后你把较大的几个数-1,然后往较小的数那里传,这样它一定阔以使每个数>=k的这个我又没想出来....)

然后,让平均数>=k,只需把每个数都减上k,使他们的和>=0即可(这个很容易想到...吧),快速维护一个区间和就用个前缀和sum就行了。

重点: 当i < j && sum[i] <= sum[j]时, i 做为左边界一定是比 j 更优的。所以我们维护一个sum单调递减的栈,在用一个指针n->0扫一遍,每次不断弹栈(不用从头重新找),更新答案

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int MAXN = 1000000+99;

int n,m;
ll a[MAXN], sum[MAXN];
int q[MAXN];
ll k;

void solve(ll k) {
	int ans = 0;
	for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+a[i]-k;
	int l = 0, r = 0;
	for(int i = 1; i <= n; i++) 
		if(sum[q[r]] > sum[i]) q[++r] = i;//l更优,所以这样做 
	for(int i = n, j = r; i >= 1; i--) {//保证i在右边 
		while(j>l && sum[i]-sum[q[j-1]] >= 0) --j;//注意判非空 
		//因为要把j左移一,所以比较的是q[j-1]的sum和i的 
		ans = max(ans, i-q[j]);
	}
	printf("%d",ans);
}

int main() {
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
	for(int i = 1; i <= m; i++) {
		scanf("%lld",&k);
		solve(k);
		if(i != m) printf(" ");
	}
}

[JLOI2012] 树

题意

给定一个值 S 和一棵树。在树的每个节点有一个正整数,问有多少条路径的节点总和为 S 。路径中节点的深度必须是升序的。
假设节点 1 是根节点,根的深度是 0,它的儿子节点的深度为1。路径不必一定从根节点开始。

分析

(不知道这题的单调队列做法,看到题解说可以用前缀和,然后就...这样了)

因为题目中限制深度必须升序,所以就是dfs到底的一条链,且每个节点都有一个正整数,所以他们的前缀和都不同,所以我们只需边dfs边往set里加sum[i], 每加入一个就在set里面找有没有值等于sum[i]-s, 统计答案即可。

注意dfs结束时erase当前节点的sum

#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
const int MAXN = 100000+99;
const int MAXM = MAXN<<1;

int n,S,ans;
int sum[MAXN];
set <int> s;

int head[MAXN], cnt;
struct seg{
	int y, next;
}e[MAXM];
void add_edge(int x, int y) {
	e[++cnt].y = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

void dfs(int x, int fa) {
	sum[x] += sum[fa]; 
	s.insert(sum[x]);
	if(s.count(sum[x]-S)) ans++;
	for(int i = head[x]; i; i = e[i].next) 
		if(e[i].y != fa) 
			dfs(e[i].y, x);
	s.erase(sum[x]);//记着清除 
}

int main() {
	scanf("%d%d",&n,&S);
	for(int i = 1; i <= n; i++) scanf("%d",&sum[i]);
	int x, y;
	for(int i = 1; i < n; i++) {
		scanf("%d%d",&x,&y);
		add_edge(x, y);
		add_edge(y, x);
	}
	s.insert(0);//有的恰为s
	dfs(1, 0);
	printf("%d",ans);
}

以上是关于栈和队列小刷的主要内容,如果未能解决你的问题,请参考以下文章

数据结构 Java数据结构 栈和队列 以及LeetCode相关面试题

栈和队列基本操作

栈和队列

博客作业03--栈和队列

博客作业03--栈和队列

栈和队列知识点总结