题目:
http://poj.org/problem?id=2796
题意:
给定一个长度为n<script type="math/tex" id="MathJax-Element-27">n</script>的数组,一个区间的值为这个区间的中所有值的和与最小值的乘积,求区间的最大值,并输出区间的左右端点
思路:
明显的单调栈,笛卡尔树也可以做
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100000 + 10, INF = 0x3f3f3f3f;
int a[N];
ll sum[N];
int top, stk[N];
int l[N], r[N];
int main()
{
int n;
while(~ scanf("%d", &n))
{
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), sum[i] = sum[i-1] + a[i];
top = 0;
for(int i = 1; i <= n; i++)
{
while(top > 0 && a[stk[top-1]] >= a[i]) top--;
l[i] = (top == 0) ? 1 : stk[top-1] + 1;
stk[top++] = i;
}
top = 0;
for(int i = n; i >= 1; i--)
{
while(top > 0 && a[stk[top-1]] >= a[i]) top--;
r[i] = (top == 0) ? n : stk[top-1] - 1;
stk[top++] = i;
}
ll ans = -1;
int tl, tr;
for(int i = 1; i <= n; i++)
{
ll tmp = (sum[r[i]] - sum[l[i]-1]) * a[i];
if(tmp > ans)
{
ans = tmp, tl = l[i], tr = r[i];
}
}
printf("%lld\n", ans);
printf("%d %d\n", tl, tr);
}
return 0;
}
笛卡尔树:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 100000 + 10, INF = 0x3f3f3f3f;
struct node
{
int val, pri, fat, id, son[2];
friend bool operator< (node a, node b)
{
return a.val < b.val;
}
void init(int _val, int _pri, int _fat, int _id)
{
val = _val, pri = _pri, fat = _fat, id = _id;
son[0] = son[1] = 0;
}
}tr[N];
int top, stk[N];
int root;
int a[N];
ll sum[N];
ll ans;
int ansl, ansr;
int cartesian_build(int n)
{
top = 0;
for(int i = 1; i <= n; i++)
{
int k = top;
while(k > 0 && tr[stk[k-1]].pri >= tr[i].pri) k--;
if(k != 0)
{
tr[stk[k-1]].son[1] = i;
tr[i].fat = stk[k-1];
}
if(k != top)
{
tr[i].son[0] = stk[k];
tr[stk[k]].fat = i;
}
stk[k++] = i;
top = k;
}
return stk[0];
}
int dfs(int x, int f)
{
if(! x) return 0;
int l = dfs(tr[x].son[0], 0);
int r = dfs(tr[x].son[1], 1);
if(l == 0) l = tr[x].id; //说明此节点管辖区间的左端点是自身
if(r == 0) r = tr[x].id; //同理
ll tmp = (sum[r] - sum[l-1]) * tr[x].pri;
if(tmp > ans) ans = tmp, ansl = l, ansr = r;
return f ? r : l; //判断返回右端点还是左端点
}
int main()
{
int n;
while(~ scanf("%d", &n))
{
tr[0].init(0, 0, 0, 0);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
sum[i] = sum[i-1] + a[i];
tr[i].init(i, a[i], 0, i);
}
sort(tr + 1, tr + 1 + n);
root = cartesian_build(n);
ans = -1;
dfs(root, 1);
printf("%lld\n", ans);
printf("%d %d\n", ansl, ansr);
}
return 0;
}