Namomo Test Round 2 C序列 题解 个人理解

题目链接
题解链接
ps:此篇博客是对题解的个人理解,为了使以后看到这道题可以看懂,如果原作者认为此篇博客侵权了,请联系我删除
题解原文:
在这里插入图片描述
个人理解:
解释一:
在这里插入图片描述
首先来个图:
在这里插入图片描述
很容易发现,如果要按最小步骤得到的最大值,那么一定是如下的图:
操作次序为:红(1次),橙(3次),黄(1次),绿(3次)
在这里插入图片描述
从这里我们就可以发现结论:前括号划分的位置恰好都是在差分( a [ i + 1 ] − a [ i ] a[i + 1] - a[i] a[i+1]a[i])为正的位置,后括号划分的位置恰好都是在差分为负的位置

而按最小步骤得到的最大值则需各位自己画图理解(ps:可以发现括号的位置都是一样的,但是顺序却不同

综上,(主要是题解 ),我们就可以大胆猜测要想用最小步骤得到代价最小值以及最大值一定要满足上述结论

简单证明为啥这样是最少步骤且能得到最大值:
假设一个全不为0的序列,那么肯定一开始要对整个序列减一,如果一开始就不这样做,那么一定不会得到最优步骤,而且值肯定也会变小…(这只是粗略证明,详细证明,我好像并不会…)

至于为啥这样是最少步骤这样也能得到最小值,请自证(真是令人讨厌的词汇

解释二:
在这里插入图片描述

  • 假设已经求出所有括号的位置,即ai, bi那么根据题意,就能得到上图那个式子,再将其简单转换一下,得到如上等式

  • 根据我们小学二年级学的排序不等式,如果想求 r e s res res最大那么一定要满足在这里插入图片描述
    其次再满足
    在这里插入图片描述
    就能得到res的最大值
    通俗来讲就是:最远的那个后括号要与最前面的前括号对应

  • r e s res res最小,请自证
    解释三:
    在这里插入图片描述
    为啥最大值要用栈来维护a呢?

  • 因为栈是先进后出,所以能保证:最远的那个后括号要与最前面的前括号对应

同理可证为啥最小值要用队列维护a

AC代码:

// Test1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
typedef pair <ll, ll> pi;
const int maxn = 500100;
const ll Mod = 1000000007;
ll a[maxn], res1, res2, n;
stack <pi> sta;//计算最大值
queue <pi> que;//计算最小值
int main() {
	ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	a[0] = a[n + 1] = 0;
	//算最小值
	for (int i = 0; i <= n; i++) {
		if (a[i + 1] - a[i] > 0) que.push(pi(i, a[i + 1] - a[i]));//前括号,队列维护
		else if (a[i + 1] - a[i] < 0) {//后括号
			ll tmp = a[i] - a[i + 1];
			while (tmp && que.front().second <= tmp) {
				res1 += que.front().second * (i - que.front().first) % Mod * (i - que.front().first) % Mod;
				res1 %= Mod;
				tmp -= que.front().second;
				que.pop();
			}
			if (tmp) {//如果不能抵消了,那么就不会弹出
				res1 += tmp * (i - que.front().first) % Mod * (i - que.front().first) % Mod;
				res1 %= Mod;
				que.front().second -= tmp;
			}
		}
	}
	//算最大值
	for (int i = 0; i <= n; i++) {
		if (a[i + 1] - a[i] > 0) sta.push(pi(i, a[i + 1] - a[i]));//栈维护
		else if (a[i + 1] - a[i] < 0) {
			ll tmp = a[i] - a[i + 1];
			while (tmp && sta.top().second <= tmp) {
				res2 += sta.top().second * (i - sta.top().first) % Mod * (i - sta.top().first) % Mod;
				res2 %= Mod;
				tmp -= sta.top().second;
				sta.pop();
			}
			if (tmp) {
				res2 += tmp * (i - sta.top().first) % Mod * (i - sta.top().first) % Mod;
				res2 %= Mod;
				sta.top().second -= tmp;
			}
		}
	}
	cout << res1 << " " << res2 << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值