0
点赞
收藏
分享

微信扫一扫

子串的最大差(单调栈)(易错)

两岁时就很帅 2022-05-02 阅读 26

每日一题 div1

Solution

a n s = ∑ ( m a x [ l , r ] − m i n [ l , r ] ) 1 ≤ l ≤ r ≤ n , [ l , r ] ans = \sum (max_{[l, r]} - min_{[l, r]})\\1 \leq l \leq r \leq n , [l, r] ans=(max[l,r]min[l,r])1lrn[l,r]表示所有子区间

  1. 去括号
    a n s = ∑ m a x [ l , r ] − ∑ m i n [ l , r ] ans = \sum max_{[l, r]} - \sum min_{[l, r]} ans=max[l,r]min[l,r]

  2. 分别求解(以max为例)

    • 找到 a [ i ] a[i] a[i] 左边第一个大于 a [ i ] a[i] a[i] 的数的下标 l [ i ] l[i] l[i] ,找到右边第一个大于 a [ i ] a[i] a[i] 的数的下标 r [ i ] r[i] r[i] (单调栈O(n))
    • a [ i ] a[i] a[i] ( l [ i ] , r [ i ] ) (l[i], r[i]) (l[i],r[i])中的最大值
    • a [ i ] a[i] a[i] 对 max的贡献为 a [ i ] × ( i − l [ i ] ) × ( r [ i ] − i ) a[i] \times (i - l[i]) \times (r[i] - i) a[i]×(il[i])×(r[i]i)
  3. 注意!
    5   1   3   3   2   4 5 ~1~ 3~ 3~ 2~ 4 5 1 3 3 2 4 为例
    a [ 3 ] = a [ 4 ] = 3 a[3] = a[4] = 3 a[3]=a[4]=3
    l [ 3 ] = l [ 4 ] = 1 l[ 3 ] = l[4] = 1 l[3]=l[4]=1       r [ 3 ] = r [ 4 ] = 6 ~~~~~r[3] = r[4] = 6      r[3]=r[4]=6
    会发现,如果按照左边取大于等于 a[i], 右边也取大于等于a[i]的值时, a[3]和a[4]有重复的贡献。(手模枚举)
    所以,可以找左边第一个大于 a[i] 的值, 右边 找第一个大于等于 a[i] 的值 。
    则 a[3]是最大值时的贡献区间: [3] , [1, 3]
    a[4]是最大值时的贡献区间:[3] , [3, 3], [3, 2], [1, 3, 3], [3, ,3 , 2], [1, 3, 3, 2 , 4]
    如此, 不重不漏。

    也可以,找左边第一个大于等于 a[i] 的值, 右边 找第一个等于 a[i] 的值 ,都可以。

Code

const int N = 5e5 + 3, M = 5e6 + 6;
ll a[N];
int l[N], r[N];
stack<int> s;

int main()
{
	IOS;
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];

	ll sum = 0;
	//找最大值
	for (int i = 1; i <= n; i++)
	{
		while (!s.empty() && a[s.top()] <= a[i]) s.pop();//注意等号取舍
		if (s.empty()) l[i] = 0;
		else l[i] = s.top();
		s.push(i);
	}
	while (s.size()) s.pop();
	for (int i = n; i >= 1; i--)
	{
		while (!s.empty() && a[s.top()] < a[i]) s.pop();//注意
		if (s.empty()) r[i] = n + 1;
		else r[i] = s.top();
		s.push(i);
	}

	for (int i = 1; i <= n; i++)//max的贡献
		sum += a[i] * (i - l[i]) * (r[i] - i);

	//找最小值
	while (s.size()) s.pop();
	for (int i = 1; i <= n; i++)
	{
		while (!s.empty() && a[s.top()] >= a[i]) s.pop();
		if (s.empty()) l[i] = 0;
		else l[i] = s.top();
		s.push(i);
	}
	while (s.size()) s.pop();
	for (int i = n; i >= 1; i--)
	{
		while (!s.empty() && a[s.top()] > a[i]) s.pop();
		if (s.empty()) r[i] = n + 1;
		else r[i] = s.top();
		s.push(i);
	}
	for (int i = 1; i <= n; i++)//min的贡献
		sum -= a[i] * (i - l[i]) * (r[i] - i);

	cout << sum << endl;


	return 0;
}
举报

相关推荐

0 条评论