CF817D Imbalanced Array&P6503 [COCI2010-2011#3] DIFERENCIJA&SP10622 DIFERENC - DIFERENCIJA 做题笔记
给出 $a_i(1\le i\le n\le 10^6)$,求$$ \sum_{i=1}^{n}\sum_{j=i}^{n}(\max_{i\le k\le j}\{a_k\}-\min_{i\le l\le j}\{a_l\}) $$
解法
化简:$$ \sum_{i=1}^{n}\sum_{j=i}^{n}\max\{a_k\}- \sum_{i=1}^{n}\sum_{j=i}^{n}\min\{a_l\} $$ 分别求值。
先处理每个区间的最大值(上面公式的前半部分)。 假设当前 $a_i$ 是某个区间 $(l,r)$ 的最大值,那么可以贪心地得到当区间长度 $r-l-1$ 最大时,$a_r\ge a_i,a_l\ge a_i$。因为如果包含 $l,r$ 那么 $a_i$ 就不是区间最大了。
最小值同理。
于是问题转换成求每个 $a_i$ 向前和向后分别求出第一个比 $a_i$ 大的数和第一个比 $a_i$ 小的数。
用单调栈扫两遍解决。
注意栈空时的特判。
代码
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int N=1145141;
int a[N],pma[N],sma[N],pmi[N],smi[N];
ll suma,sumi;
int main()
{
int n;
scanf("%d",&n);
vector<int> ma,mi;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
}
for(int i=1;i<=n;i++)
{
while(!ma.empty()&&a[ma.back()]<a[i])
{
ma.pop_back();
}
if(ma.empty())// 特判后赋值,下同
{
pma[i]=0;
}
else
{
pma[i]=ma.back();
}
ma.push_back(i);
while(!mi.empty()&&a[mi.back()]>a[i])
{
mi.pop_back();
}
if(mi.empty())
{
pmi[i]=0;
}
else
{
pmi[i]=mi.back();
}
mi.push_back(i);
}
mi.clear();
ma.clear();
for(int i=n;i>0;i--)
{
while(!ma.empty()&&a[ma.back()]<=a[i])
{
ma.pop_back();
}
if(ma.empty())
{
sma[i]=n+1;
}
else
{
sma[i]=ma.back();
}
ma.push_back(i);
while(!mi.empty()&&a[mi.back()]>=a[i])
{
mi.pop_back();
}
if(mi.empty())
{
smi[i]=n+1;
}
else
{
smi[i]=mi.back();
}
mi.push_back(i);
}
for(int i=1;i<=n;i++)
{
suma+=(a[i]*(i-pma[i])*(sma[i]-i));
sumi+=(a[i]*(i-pmi[i])*(smi[i]-i));
}
printf("%lld",suma-sumi);
return 0;
}