[POJ2796]Feel Good
题目传送门:http://poj.org/problem?id=2796
这道题的话我用了RMQ和二分。
思想:用minm来表示区间[L,R]的最小值,很明显一个区间[L,R]能取得的最大值,要么就是[L,minm-1],要么就是[minm+1,R],还有就是整个区间的和乘最小值。
因为对于某两个最小值相等的区间来说(题目说明是非负整数),显然越大的区间取得的最大值越大。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int maxn=100010; int num[maxn],n,sum[maxn],fmin[maxn][20],fmax[maxn][20]; long long ans=0,ansl,ansr; int querymin(int l,int r) { int k=0; if (l>r)return 0; while (1<<(k+1)<=r-l+1)k++; return min(fmin[l][k],fmin[r-(1<<k)+1][k]); } int querymax(int l,int r) { int k=0,tmp=0; if (l>r)return 0; while (1<<(k+1)<=r-l+1)k++; tmp=max(fmax[l][k],fmax[r-(1<<k)+1][k]); tmp=max(tmp,(sum[r]-sum[l-1])*querymin(l,r)); if (tmp>ans) { ans=tmp; ansl=l; ansr=r; } return tmp; } void findmin() //用RMQ求第i个到第j个这个区间的最小值是哪一个 { int i,j; memset(fmin,0x7f,sizeof(fmin)); for (i=1;i<=n;i++)fmin[i][0]=i; for (j=1;(1<<j)<=n;j++) for (i=1;i+(1<<j)-1<=n;i++) if (num[fmin[i][j-1]]<num[fmin[i+(1<<(j-1))][j-1]])fmin[i][j]=fmin[i][j-1]; else fmin[i][j]=fmin[i+(1<<(j-1))][j-1]; } void findmax() //表示求第i个到第j个这个区间能取得的最大值 { int i,j,l,r; memset(fmax,0,sizeof(fmax)); for (i=1;i<=n;i++)fmax[i][0]=num[i]*num[i]; for (j=1;(1<<j)<=n;j++) for (i=1;i+(1<<j)-1<=n;i++) { r=i+(1<<j)-1; l=querymin(i,r); fmax[i][j]=max(querymax(i,max(i,l-1)),querymax(max(l+1,r),r)); fmax[i][j]=max(fmax[i][j],(sum[r]-sum[i-1])*num[l]); if (fmax[i][j]>ans) { ans=fmax[i][j]; ansl=i; ansr=r; } } } int main() { int i,j; scanf("%d",&n); sum[0]=0; for (i=1;i<=n;i++) { scanf("%d",&num[i]); sum[i]=sum[i-1]+num[i]; } findmin(); findmax(); cout<<ans<<endl<<ansl<<" "<<ansr<<endl; return 0; }