XTU 1205 Range 2014湖南邀请赛C 单调栈
ACM
题目地址:XTU 1205
题意:
在一个序列A中,Range(A)=Max(A)-Min(A)+1;
求一个序列的子序列中所有子序列的Range的和。
分析:
做了好久。。。多谢Tamara巨巨的指点。
1 2 3 4 5
Range(A)=Max(A)-Min(A)+1;
先别看后面的+1
统计每一位会增加的次数以及会减少的次数。
-
先看会增加的,x表示左边小的个数,y表示右边小或等于的个数
当i=1, x = 0, y = 0, det = 0
当i=2, x = 1, y = 0, det = (1+0+0)*2
当i=3, x = 2, y = 0, det = (2+0+0)*3
当i=4, x = 3, y = 0, det = 3*4
当i=5, x = 4, y = 0, det = 4*5 -
再看会减少的,x表示左边大的个数,y表示右边大或等于的个数
当i=1, x = 0, y = 4, det = 4*1
当i=2, x = 0, y = 3, det = 3*2
当i=3, x = 0, y = 2, det = 2*3
当i=4, x = 0, y = 1, det = 1*4
当i=5, x = 0, y = 0, det = 0*5
所以,ans=2+6+12+20-4-6-6-4=20
由于还要+1,所以还要加n*(n+1)/2。
我们只要用单调栈求出需要的x和y就行了。
代码:
/*
* Author: illuz <iilluzen[at]gmail.com>
* File: C.cpp
* Create Date: 2014-06-07 15:41:15
* Descripton:
*/
#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
stack<int> s, ss;
int t, n;
int a[N], big[N], bige[N];
ll ans, tmp;
int main()
{
scanf("%d", &t);
for (int cas = 1; cas <= t; cas++) {
scanf("%d", &n);
while (!s.empty())
s.pop();
while (!ss.empty())
ss.pop();
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
// 左边小的个数
while (!s.empty() && a[s.top()] > a[i])
s.pop();
if (s.empty())
big[i] = i;
else
big[i] = i - s.top() - 1;
s.push(i);
// 左边大的个数
while (!ss.empty() && a[ss.top()] < a[i])
ss.pop();
if (ss.empty())
bige[i] = i;
else
bige[i] = i - ss.top() - 1;
ss.push(i);
}
while (!s.empty())
s.pop();
while (!ss.empty())
ss.pop();
ans = 0;
for (int i = n - 1; i >= 0; i--) {
// 右边小或等于的个数
while (!s.empty() && a[s.top()] >= a[i])
s.pop();
if (s.empty())
tmp = n - i - 1;
else
tmp = s.top() - i - 1;
ans -= (tmp + big[i] + tmp * big[i]) * a[i];
s.push(i);
// 右边大或等于的个数
while (!ss.empty() && a[ss.top()] <= a[i])
ss.pop();
if (ss.empty())
tmp = n - i - 1;
else
tmp = ss.top() - i - 1;
ans += (tmp + bige[i] + tmp * bige[i]) * a[i];
ss.push(i);
}
ans += n * (n + 1) / 2;
printf("Case %d: ", cas);
cout << ans << endl;
}
return 0;
}