题意:
给定一个长度为n的数组a
a中一个连续区间的strength是区间内的最小值,对x=1,2,...,n分别求长度为x的连续区间中,strength的最大值是多少
思路:
对于每个a[i]找出在a[i]左边,离a[i]最近且比a[i]小的数的下标,记为VL[i],若不存在则VL[i]=0;
找出在a[i]右边,离a[i]最近且比a[i]小的数的下标,记为VR[i],若不存在则VR[i]=n+1;
(VL和VR数组可以用栈计算出,具体直接见代码)
那么,a[i]是区间(VL[i],VR[i])内的最小值,也就是说,a[i]会出现在长度为1,2,...,VR[i]-VL[i]-1 的组的strength中,于是就用a[i]去更新这些最大值
易知a[i]不会成为长度大于VR[i]-VL[i]-1的区间的最小值,也就不需要更新这些最大值。
现在给定一个数列ans[1],ans[2],..,ans[n],初值为0
对于每个a[i],让区间[1,VR[i]-VL[i]-1]内的ans都与a[i]取最大值
要实现这个操作,可以用线段树。
在所有操作结束后一次性下传所有最大值标记,然后输出最后的ans数组即可。
看题解,第二点是不需要线段树来实现的,第二步可以做到O(n)
首先用ans[i]代表从i往左全部都与ans[i]进行max操作,这就相当于差分的思想
于是,前面第一步,直接令ans[ VR[i]-VL[i]-1 ]与a[i]进行max操作
然后倒着线性递推一遍ans[i]=max(ans[i],ans[i+1])就可以了。
线段树代码(6400KB 124ms):
#include <iostream> #include <cstdio> #include <cmath> #include <stack> #define maxn 200007 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 using namespace std; int n; int a[maxn]; int vl[maxn]; int vr[maxn]; stack<int> s; int ans[maxn<<2]; void PushDown(int rt){ if(ans[rt]){ ans[rt<<1]=max(ans[rt],ans[rt<<1]); ans[rt<<1|1]=max(ans[rt],ans[rt<<1|1]); ans[rt]=0; } } void build(int l,int r,int rt){ if(l==r){ ans[rt]=0; return; } int m=(l+r)>>1; build(ls); build(rs); ans[rt]=0; } void Add(int L,int R,int C,int l,int r,int rt){ if(L <= l && r <= R){ ans[rt]=max(ans[rt],C); return; } int m=(l+r)>>1; if(L <= m) Add(L,R,C,ls); if(R > m) Add(L,R,C,rs); } void PushDownAll(int l,int r,int rt){ if(l==r) { a[l]=ans[rt]; return; } int m=(l+r)>>1; PushDown(rt); PushDownAll(ls); PushDownAll(rs); } int main(void) { while(~scanf("%d",&n)){ for(int i=1;i<=n;++i) scanf("%d",&a[i]); while(!s.empty()) s.pop();//初始化vl数组 for(int i=1;i<=n;++i){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) vl[i]=0; else vl[i]=s.top(); s.push(i); } while(!s.empty()) s.pop();//初始化vr数组 for(int i=n;i>=1;--i){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) vr[i]=n+1; else vr[i]=s.top(); s.push(i); } build(1,n,1);//初始化线段树 for(int i=1;i<=n;++i){ Add(1,vr[i]-vl[i]-1,a[i],1,n,1);//操作 } PushDownAll(1,n,1);//下传所有标记并将结果存入a[i] for(int i=1;i<=n;++i){//输出答案 printf("%d",a[i]); if(i==n) printf("\n"); else printf(" "); } } return 0; }
#include <iostream> #include <cstdio> #include <cmath> #include <stack> #include <cstring> #define maxn 200007 using namespace std; int n; int a[maxn]; int vl[maxn]; int vr[maxn]; stack<int> s; int ans[maxn]; int main(void) { while(~scanf("%d",&n)){ for(int i=1;i<=n;++i) scanf("%d",&a[i]); while(!s.empty()) s.pop();//初始化vl数组 for(int i=1;i<=n;++i){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) vl[i]=0; else vl[i]=s.top(); s.push(i); } while(!s.empty()) s.pop();//初始化vr数组 for(int i=n;i>=1;--i){ while(!s.empty()&&a[s.top()]>=a[i]) s.pop(); if(s.empty()) vr[i]=n+1; else vr[i]=s.top(); s.push(i); } memset(ans,0,sizeof(ans)); for(int i=1;i<=n;++i){ int maxLen=vr[i]-vl[i]-1; ans[maxLen]=max(ans[maxLen],a[i]); } for(int i=n-1;i>0;--i) ans[i]=max(ans[i],ans[i+1]); for(int i=1;i<=n;++i){//输出答案 printf("%d",ans[i]); if(i==n) printf("\n"); else printf(" "); } } return 0; }