我们定义“区间的价值”为一段区间的最大值*最小值。
一个区间左端点在L,右端点在R,那么该区间的长度为(R-L+1)。
现在聪明的杰西想要知道,对于长度为k的区间,最大价值的区间价值是多少。
当然,由于这个问题过于简单,我们肯定得加强一下。
我们想要知道的是,对于长度为1~n的区间,最大价值的区间价值分别是多少。
样例解释:
长度为1的最优区间为2-2 答案为6*6
长度为2的最优区间为4-5 答案为4*4
长度为3的最优区间为2-4 答案为2*6
长度为4的最优区间为2-5 答案为2*6
长度为5的最优区间为1-5 答案为1*6
输入
多组测试数据
第一行一个数n。
第二行n个正整数,下标从1开始。
由于某种不可抗力,ai的值将会是[1, 10^9]内随机产生的一个数。(除了样例)
输出
输出共n行,第i行表示区间长度为i的区间中最大的区间价值。
样例输入
5
1 6 2 4 4
样例输出
36
16
12
12
6
提示
1≤n≤100000 1≤ai≤10^9
考试的时候死活想不出正解,我果然还是too young too simple。
题解传送门
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
#define p1 id<<1
#define p2 id<<1^1
using namespace std;
int n;
ll a[100005],ans[100005];
struct ty
{
int min,max;
}tree[400005];
void build(int id,int l,int r)
{
if(l==r)
{
tree[id].min=l; //记录区间min和max的下标
tree[id].max=l;
return;
}
int mid=(l+r)/2;
build(p1,l,mid);
build(p2,mid+1,r);
if(a[tree[p1].min]<a[tree[p2].min]) tree[id].min=tree[p1].min; else tree[id].min=tree[p2].min;
if(a[tree[p1].max]>a[tree[p2].max]) tree[id].max=tree[p1].max; else tree[id].max=tree[p2].max;
}
int querymin(int id,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return tree[id].min;
int mid=(l+r)/2;
if(y<=mid) return querymin(p1,l,mid,x,y);
else
if(x>mid) return querymin(p2,mid+1,r,x,y);
else
{
int q=querymin(p1,l,mid,x,mid),e=querymin(p2,mid+1,r,mid+1,y);
if(a[q]<a[e]) return q; else return e;
}
}
int querymax(int id,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return tree[id].max;
int mid=(l+r)/2;
if(y<=mid) return querymax(p1,l,mid,x,y);
else
if(x>mid) return querymax(p2,mid+1,r,x,y);
else
{
int q=querymax(p1,l,mid,x,mid),e=querymax(p2,mid+1,r,mid+1,y);
if(a[q]>a[e]) return q; else return e;
}
}
void solve(int l,int r)
{
int mi=querymin(1,1,n,l,r),ma=querymax(1,1,n,l,r);
if(mi>ma) swap(mi,ma);
for(int i=ma-mi+1;i<=r-l+1;i++) ans[i]=max(ans[i],a[mi]*a[ma]); //暴力更新答案
if(a[mi]<a[ma]) //以最小值为中点,分割成两个区间
{
if(l<=mi-1) solve(l,mi-1);
if(mi+1<=r) solve(mi+1,r);
}
else
{
if(l<=ma-1) solve(l,ma-1);
if(ma+1<=r) solve(ma+1,r);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans[i]=0;
}
build(1,1,n);
solve(1,n);
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
}
return 0;
}
以最小值的下标分割成两个区间的正确性证明:
如果不是这样分割的话,势必仍将包括最小值,那么最大值必<=[l,r]中的最大值,乘积肯定更小。