poj2796
#include <cstdio> /* * source poj.2796 * 题目: * 给定一个非负数的数组 其中value[l,r] = sum(l,r) * min (l,r); * 求 最大值和,最大值的位置 * 题解: * 所求的区域的最小值是x的话一定是这个值向左右去延伸至比他大的元素为止 * 而这个问题的求解一般是n^2的问题,但是我们不能接受因此; * 维护这个区间需要我们维护一个stack * 具体操作如下 * (1)元素入栈 * 1,记录值(value, weight) -> weight 初始是1,value是入栈的值, * 2,先让大于等于value的值出栈,并且更新 ANS [value * weight] 对比决定是否更新. * 3,如果这个栈顶元素也大于等于value, 把出栈的这个值的重量给目前的栈顶元素, 到步骤(2),否则到步骤(4). * 4,把这个出栈的重量给 准备入栈的元素. * 5,元素入栈 * (2)元素全部入栈后 因为还有元素在里面. 剩下的元素逐个出栈, 只不过把这个元素的重量给下一个栈顶元素. 类似与插入-1. * hint: * * 其实他每次出栈的 就是我们刚开说的那种区间, 以那个元素为做小值向左右扩展得到的区间值. * * 对于栈顶元素. * 一个元素的入栈 会促使 左侧比他大的合并, * 而后出栈的时候右侧肯定也都合并到自己身上,因此栈顶元素对于我们所扫描到的位置一定是合法的 * 栈顶元素肯定向右或者说向左都是达标的; * 这是个斜率优化问题. 怀念以前,现在是个弱鸡(ง •̀_•́)ง */ #define min(x, y) ( (x) < (y) ? (x) : (y) ) #define max(x, y) ( (x) < (y) ? (y) : (x) ) const int N = 1e5; struct Ans { int l, r; long long value; Ans(){l = r = value = -1;} bool operator < (const Ans & rht ) const { return value < rht.value; } void out(){ printf("%lld\n%d %d\n", value, l, r); } void show(int i) { printf("i = %d %d %d %lld \n", i, l, r, value); } }; struct Info { long long h, w; int p; Info(){} Info(long long _h, long long _w, int _p):h(_h), w(_w), p(_p) {} bool operator < (const Info & rht) const { return this -> h < rht.h; } Info operator + (const Info & rht) const { return Info( min(rht.h, this -> h), rht.w + this -> w, this -> p); } }; Info stack[N + 7]; int pos; Ans ans; inline void init() { pos = 0; ans = Ans(); } void in(long long tmp, int i) { Info inStack = Info(tmp, tmp, i); Info key = Info(0x7fffffff, 0, -1); while(pos != 0 && stack[pos - 1].h >= inStack.h) { key = stack[--pos] + key; Ans wps = Ans(); wps.l = key.p; wps.r = i - 1; wps.value = (long long )key.w * (long long )key.h; ans = max(ans, wps); } if (key.p != -1) { inStack = key + inStack; } stack[pos++] = inStack; } int main() { int n; long long tmp; while(~scanf("%d", &n)) { init(); for(int i = 1; i <= n; ++i) { scanf("%lld", &tmp); in(tmp, i); } in(0, n + 1); ans.out(); } return 0; }