HDU 5696 区间的价值

我们定义“区间的价值”为一段区间的最大值*最小值。
一个区间左端点在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]中的最大值,乘积肯定更小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值