题目大意
给你一个数n和长度为n的序列,序列中的每个数均为1或-1,如果一个点j对于任意的k都满足题目中给的式子,则j是一个合法位置,问这样的j有多少个
分析
这道题有两种方法,分别对应代码1和代码2。
方法1
我们发现最终答案实际就是这n个数的和与0去最大值,因为我们不难发现对于任意两个相邻的1和-1它们的存在是没有意义的,因为这两个位置肯定不合法,而它们的和为0,对其它位置没有影响。所以在得到这些后我们只需把相邻的1和-1不断删掉就行了,因此可以证明之前的猜想。
方法2
我们发现对于每个j对应的最坏情况的k只有图1和图2两种情况,所以只要这两种情况满足且j的后缀大于0这个j就是合法的。
图1图2
这里的maxsur和minpre都是对于k的取值范围内的min或max,这就不由让我们想起了单调队列。我们根据sur和pre各建一个单调队列,然后用数组记录某个点是否两种情况均满足就可以了,注意在计算时的顺序问题,详见代码。
代码1
#include<bits/stdc++.h>
using namespace std;
int main(){
freopen("positive.in","r",stdin);
freopen("positive.out","w",stdout);
int n,i,ans=0;
cin>>n;
for(i=1;i<=n;i++){
int x;
cin>>x;
ans+=x;
}
if(ans>=0)cout<<ans<<endl;
else cout<<0<<endl;
return 0;
}
代码2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
int pre[200100],sur[200100],qp[200100],qs[200100],lp,rp,ls,rs,a[200100];
int p1[200100],p2[200100],is[200100];
inline void init(){
memset(pre,0,sizeof(pre));
memset(sur,0,sizeof(sur));
memset(qp,0,sizeof(qp));
memset(qs,0,sizeof(qs));
memset(is,0,sizeof(is));
lp=ls=1;
rp=rs=0;
}
int main(){
freopen("positive.in","r",stdin);
freopen("positive.out","w",stdout);
int n,i,ans=0;
init();
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=pre[i-1]+a[i];
}
for(i=n;i>0;i--)sur[i]=sur[i+1]+a[i];
for(i=n;i>0;i--){
while(pre[i]<qp[rp]&&rp>0){
rp--;
}
qp[++rp]=pre[i];
p1[rp]=i;
}
for(i=1;i<=n;i++){
while(sur[i]>qs[rs]&&rs>0){
rs--;
}
qs[++rs]=sur[i];
p2[rs]=i;
}
for(i=1;i<=n;i++){
while(p2[ls]<=i&&ls<=rs)ls++;
if(ls>rs){
if(sur[i]>0)is[i]++;
continue;
}
if(sur[i]-qs[ls]>0)is[i]++;
}
for(i=n;i>0;i--){
while(p1[lp]>=i&&lp<=rp)lp++;
if(lp>rp){
if(sur[i]>0)is[i]++;
continue;
}
if(sur[i]+qp[lp]>0)is[i]++;
}
for(i=1;i<=n;i++)
if(sur[i]>0&&is[i]==2)
ans++;
cout<<ans<<endl;
return 0;
}