牛牛的滑动窗口

题面

链接:https://ac.nowcoder.com/acm/contest/7604/D

牛牛最近学习了滑动窗口类的算法,滑动窗口算法可以解决一些线性数组的离线静态区间查询类问题。

具体来说,假设对于一个数组进行m次静态区间查询问题。如果这些查询满足条件:i,jlilj时,一定满足rirj(i,j表示查询的编号,l,r表示查询的左右端点)

接下来只要查询的问题满足可以快速插入和删除单点,就可以使用滑动窗口优化,将这m次查询的复杂度降低到O(n)。

牛牛接下来想要问你的问题也和定长滑动窗口有关。

众所周知,长度为k的滑动窗口从左到右去截取一个长度大小为n的数组时,一共可以截取到n-k+1个子数组。

牛牛将这n-k+1个子数组的极大值与极小值的乘积求和称为该数组的"第k窗口值"。

举个例子,假设长度为5的数组为[1,5,2,4,3],长度为3的滑动窗口可以截取三个子数组,它们分别为[1,5,2],[5,2,4],[2,4,3]。

所以该数组的“第3窗口值”为1*5+2*5+2*4=23。

对于一个给定大小的数组n,牛牛现在想要知道它的第1,2,3,4,5...n窗口值各是多少,请你编写程序解决他的问题。

1n105,1ai102

解法

朴素的算法就是用单调队列O(N)模拟每个区间长度下的值之和,算法O(N2)

但很显然这个算法过不了,原因在于ai很小,这就意味着能取到的贡献值不会超过104,这也意味着有很多区间其实它们的值是一样的。

比如样例

5 
1 5 2 4 3

可以发现区间[1 5]、[1 5 2]、[1 5 2 4]、[1 5 2 4 3]它们的贡献都是1×5=5,而朴素算法把它们都算了一遍,事实上我们只需要给区间长度在2和5之间的位置加上5,也可以达到同样的效果。

于是想到先固定左端点,然后再统计一些右端点的答案。

假如当前固定左端点为i,而i后面第一个比ai大的位置在k,第一个比它小的位置在j,那么max(as)=ai,s[i,k1],同理min(as)=ai,s[i,j1]。那么很明显,所有以i为左端点、r(r[j1,k1])为右端点的区间所产生的贡献都是ai×ai,换言之,所有长度为len(len[1,min(ji,ki)])的答案都应该加上ai×ai

然后继续往后扫描,重复比较即可。

#include<cstdio>
#include<stack>
#define ll long long
//#define zczc
using namespace std;
const int N=100010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}

inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}

int m,a[N];
#define lowbit (wh&-wh)
ll t[N];
inline void ch(int wh,ll val){
	for(;wh<=m;wh+=lowbit)t[wh]+=val;
}
inline void change(int l,int r,ll val){
	//printf("change:%d %d %lld\n",l,r,val);
	ch(l,val);ch(r+1,-val);return;
}
inline ll work(int wh){
	ll an=0;
	for(;wh;wh-=lowbit)an+=t[wh];
	return an;
}
#undef lowbit

struct node{
	int num,data;
};
stack<node>st;
int firmin[N],firmax[N];

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);
	for(int i=1;i<=m;i++)read(a[i]);
	firmin[m+1]=firmax[m+1]=m+1;
	st.push((node){m+1,101});
	for(int i=m;i;i--){
		while(st.top().data<=a[i])st.pop();
		firmax[i]=st.top().num;
		st.push((node){i,a[i]});
	}
	while(!st.empty())st.pop();
	st.push((node){m+1,0});
	for(int i=m;i;i--){
		while(st.top().data>=a[i])st.pop();
		firmin[i]=st.top().num;
		st.push((node){i,a[i]});
	}
	for(int i=1;i<=m;i++){
		int nl=i,lmax=firmax[i],lmin=firmin[i],nmax=a[i],nmin=a[i];
		while(nl<=m){
			change(nl-i+1,min(lmax,lmin)-i,(ll)nmax*nmin);
			nl=min(lmax,lmin);
			if(lmin<lmax){
				nmin=a[lmin];
				lmin=firmin[lmin];
			}
			else{
				nmax=a[lmax];
				lmax=firmax[lmax];
			}
		}
	}
	for(int i=1;i<=m;i++)printf("%lld ",work(i));
	
	return 0;
}
posted @   Feyn618  阅读(249)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示