栈和队列
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);
}