ZCMU 1985:小C的数学问题(线段树+分治)

来源:互联网 发布:java for循环递归算法 编辑:程序博客网 时间:2024/05/22 06:04

小C的数学问题

Time limit:1000ms Memory limit:128 MB


Problem Description

小C是个云南中医学院的大一新生,在某个星期二,他的高数老师扔给了他一个问题。
让他在1天的时间内给出答案。
但是小C不会这问题,现在他来请教你。
请你帮他解决这个问题。
有n个数,每个数有权值。
数学老师定义了区间价值为区间和乘上区间内的最小值。
现在要你找出有最大区间价值的区间是什么,并输出区间价值。

Input

每个输入文件只包含单组数据。
第一行一个整数n。(1 <= n <= 100000)
na1,a2,...,an(0<=ai<=1000000)

Output

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

Sample Input

610 1 9 4 5 9

Sample Output

1083 6

解题思路:

题目不算难,但是比赛两个多小时就是没想出来。。。
对于每个区间,自身的值为区间和*区间最小值,根据区间最小值的位置pos,可以把区间分成左右两边
总结一下,对于每个区间有 1.当前区间的值,2.pos左边区间的最大值,3.pos右边区间的最大值,三个值取最大值就是当前区间能得到的最大值。
然后每次得到最大值的时候都去与ans比较一下,如果比当前ans大,则记录区间位置。
区间和可以用前缀数组,区间最小值和它的pos则用线段树来找(返回类型为pair),复杂度是O(logN)

Code:

#include <iostream>#include <cstdio>using namespace std;typedef long long LL;const int MAXN=100000+5;LL a[MAXN],sum[MAXN];int st=1,ed=1;LL ans=0;struct node{    int id;    int left,right;    LL min;}num[MAXN*4];pair<int,LL> buildmin(int left,int right,int cnt){    int mid;    num[cnt].left=left;    num[cnt].right=right;    if(left==right)    {        num[cnt].id=left;        num[cnt].min=a[left];        return make_pair(left,a[left]);    }    mid=(left+right)>>1;    pair<int,LL> r1=buildmin(left,mid,cnt*2);    pair<int,LL> r2=buildmin(mid+1,right,cnt*2+1);    if(r1.second<r2.second)    {        num[cnt].id=r1.first;        num[cnt].min=r1.second;        return r1;    }    else    {        num[cnt].id=r2.first;        num[cnt].min=r2.second;        return r2;    }}//返回pair类型,first为下标,second为最小值pair<int,LL> querymin(int left,int right,int cnt){    int mid;    if(left==num[cnt].left&&right==num[cnt].right)        return make_pair(num[cnt].id,num[cnt].min);    mid=(num[cnt].left+num[cnt].right)>>1;    if(right<=mid)        return querymin(left,right,cnt*2);    else if(left>mid)        return querymin(left,right,cnt*2+1);    else    {        pair<int,LL> r1=querymin(left,mid,cnt*2);        pair<int,LL> r2=querymin(mid+1,right,cnt*2+1);        return r1.second<r2.second?r1:r2;    }}//分治查询LL f(int l,int r){    if(l>r)        return 0;    if(l==r)        return a[l]*a[l];    pair<int,LL> p=querymin(l,r,1);    int pos=p.first;    LL m=p.second;    LL res=(sum[r]-sum[l-1])*m;    if(res>ans)    {        ans=res;        st=l,ed=r;    }    return max(res,max(f(l,pos-1),f(pos+1,r)));}int main(){    int n;    scanf("%d",&n);    for(int i=1; i<=n; i++)    {        scanf("%lld",&a[i]);        sum[i]=sum[i-1]+a[i];    }    buildmin(1,n,1);    printf("%lld\n",f(1,n));    printf("%d %d\n",st,ed);    return 0;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 主卧别人睡过了怎么办 卧室门对着过道怎么办 有墙角对着窗户怎么办 卧室门对着墙角怎么办 冰箱和大门对着怎么办 冰箱对着入户门怎么办 哪来的大路对门怎么办 卧室窗户对着路怎么办 床尾对着窗户怎么办 床头对着厕所门怎么办 5楼阳台对着马路怎么办 宿舍门对着楼梯怎么办 如果镜子对着门怎么办 厕所门对着厨房怎么办 客厅门对着大门怎么办 床头对着厕所墙怎么办 被小狗舔了伤口怎么办 狗狗伤口好不了怎么办 狗的伤口发炎了怎么办 大门正对房门该怎么办 房门正对厕所门怎么办 浴室镜子对着门怎么办 房间门正对厕所怎么办 厕所斜对着大门怎么办 刚开麻将馆没人怎么办 入户门正对电梯怎么办 电梯对着入户门怎么办 电梯门对着房门怎么办 厨房门对入户门怎么办 餐桌对着入户门怎么办 房门正对楼梯下怎么办 电梯门对着家门怎么办 壁纸上的泡沫胶怎么办 卧室镜子对着门 怎么办 电视对着厨房门怎么办 厨房门对着大门怎么办 厨房对着入户门怎么办 入户门正对厨房怎么办 入户门对厨房门怎么办 入户门正对窗户怎么办 入户门对着餐厅怎么办