区间最小值*区间和 线段树+二分

 小C的数学问题

时间限制: 1 Sec  内存限制: 128 MB
提交: 203  解决: 44
[提交] [状态] [命题人:外部导入]

题目描述

小C是个云南中医学院的大一新生,在某个星期二,他的高数老师扔给了他一个问题。

让他在1天的时间内给出答案。

但是小C不会这问题,现在他来请教你。

请你帮他解决这个问题。

有n个数,每个数有权值。

数学老师定义了区间价值为区间和乘上区间内的最小值。

现在要你找出有最大区间价值的区间是什么,并输出区间价值。

 

输入

每个输入文件只包含单组数据。
第一行一个整数n。(1 <= n <= 100000)
第二行n个整数a_1,a_2,...,a_n。(0 <= a_i <= 1000000)

 

输出

第一行输出一个整数,表示最大的区间价值。
第二行输出两个整数,表示区间的起点和终点。
保证答案唯一。

 

样例输入

复制样例数据

6
10 1 9 4 5 9

样例输出

108
3 6

查询区间值用线段树(这里说一下,如果不用线段树直接上来二分,每一次遍历去查找最小值会tle)

之后整体采用二分思想,比较当前区间、左区间和右区间。

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define Max 100005
#define ll long long
ll sum[Max];
struct data
{
    ll l,r,minnum;
    ll minloc;
} node[4*Max];

ll score[Max];
ll ansl=0,ansr=0,last=-1;
ll min(ll a,ll b)
{
    return a<b?a:b;
}
void Buildnode(ll left,ll right,ll num)
{
    node[num].l=left;
    node[num].r=right;
    if(left==right)
    {
        node[num].minnum=score[left];
        node[num].minloc=left;
        return;
    }
    else
    {
        Buildnode(left,(left+right)>>1,2*num);
        Buildnode(((left+right)>>1)+1,right,2*num+1);
        if(node[2*num].minnum<node[2*num+1].minnum)
            node[num].minnum=node[2*num].minnum,node[num].minloc=node[2*num].minloc;
        else
            node[num].minnum=node[2*num+1].minnum,node[num].minloc=node[2*num+1].minloc;
    }
}
ll loc,N;
ll Query(ll left,ll right,ll num)
{
    if(node[num].l==left && node[num].r==right){
        return node[num].minnum;
    }

    if(right<=node[2*num].r)
        return Query(left,right,2*num);


    if(left>=node[2*num+1].l)
        return Query(left,right,2*num+1);

    ll mid=(node[num].l+node[num].r)>>1;
    return min(Query(left,mid,2*num),Query(mid+1,right,2*num+1));
}
ll deal(ll left,ll right){
    if(left>right)
        return 0;
    if(left==right)
    {
        ll now=score[left]*score[left];
        if(now>last)
        {
            last=now;
            ansl=left,ansr=right;
        }
        return now;
    }
    ll minnum=Query(left,right,1);
    ll now=(sum[right]-sum[left-1])*minnum;
    if(now>last){
        last=now;
        ansl=left,ansr=right;
    }
    int pos;
    for(ll i=left;i<=right;i++)
    {
        if(score[i]==minnum){
            pos=i;
            break;
        }
    }
    return max(now,max(deal(left,pos-1),deal(pos+1,right)));
}
int  main()
{
    ll M;
    while(scanf("%lld",&N)!=EOF)
    {
        ll i;
        memset(sum,0,sizeof(sum));
        for(i=1; i<=N; i++)
        {
            scanf("%lld",&score[i]);
            sum[i]=sum[i-1]+score[i];
        }
        Buildnode(1,N,1);
        deal(1,N);
        printf("%lld\n",last);
        printf("%lld %lld\n",ansl,ansr);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值