2020牛客NOIP赛前集训营-普及组(第一场)D-牛牛的滑动窗口
牛牛的滑动窗口
https://ac.nowcoder.com/acm/contest/7604/D
前言
我严重怀疑这道题来错地方了QwQ,看题解和代码看了半天
分析
首先是暴力的n^2滑动窗口做法,枚举区间长度,然后做两次单调队列求极值,求出答案
int n,m; int q1[N],q2[N],a[N],b[N]; inline void min_deque() { int h=1,t=0; for(int i=1;i<=n;i++) { while(h<=t&&q1[h]+m<=i) h++; while(h<=t&&a[i]<a[q1[t]]) t--; q1[++t]=i; b[i]=a[q1[h]]; } } inline int max_deque() { int h=1,t=0,ans=0; for(int i=1;i<=n;i++) { while(h<=t&&q2[h]+m<=i) h++; while(h<=t&&a[i]>a[q2[t]]) t--; q2[++t]=i; if(i>=m) ans+=a[q2[h]]*b[i]; } return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) { m=i; min_deque(); printf("%d ",max_deque()); } return 0; }既然正解是从普通的滑动窗口得来的,那就让我们
脑胡一下这个过程。因为对于每一个点,都能把他作为某个K滑动窗口的起点。
1滑动窗口:[1,1][2,2][3,3][...][n,n]
2滑动窗口:[1,2][2,3][3,4][...][n-1,n]
3滑动窗口:[1,3][2,4][3,5][...][n-2,n]
...
竖着看。那么这就是我们将每一个点作为起点求出他对相关K滑动窗口的贡献的理由(注意断句)。对于某一些区间,其实他们的最大值和最小值是相等的。那么只要从起点开始,找到这些区间,将他们的贡献加入差分数组,最后统一计算即可。
就像这样,我们不去考虑某一个区间应该加上哪些值,而是考虑一个值可以对多少区间产生影响。
代码
/*
可以确定的是,第i个数的ma与mi
其中一个一定等于i+1
以每一个点为起点,计算之后
的贡献
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n;
ll a[N],stk[N],ki[N],ka[N],ans[N];
//值数组, 栈数组,最大扩展区间,最小扩展区间
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
//求最大值
for (int i=n,tp=0;i>=1;i--)
{
while(tp&&a[stk[tp]]<=a[i]) tp--;
if(tp) ka[i]=stk[tp];else ka[i]=n+1;
stk[++tp]=i;
}
//求最小值
for (int i=n,tp=0;i>=1;i--)
{
while(tp&&a[stk[tp]]>=a[i]) tp--;
if(tp) ki[i]=stk[tp];else ki[i]=n+1;
stk[++tp]=i;
}
for (int i=1;i<=n;i++)
{
ll ma=i,mi=i,lmin=i,rmin=ki[i]-1,lmax=i,rmax=ka[i]-1;
while(1)
{
int l=lmin-i+1,r=min(rmin,rmax)-i+1;
ans[l]+=a[ma]*a[mi];ans[r+1]-=a[ma]*a[mi];
lmin=lmax=min(rmin,rmax)+1;
if(lmin==n+1) break;
if(rmin<lmin) mi=rmin+1,rmin=ki[mi]-1;
if(rmax<lmax) ma=rmax+1,rmax=ka[ma]-1;
}
}
for (int i=1;i<=n;i++) ans[i]+=ans[i-1];
for (int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}比赛题解 文章被收录于专栏
牛客IOI周赛,团队赛,练习赛,挑战赛,各种模拟赛的部分题解
查看20道真题和解析

