思路:
首先我们考虑到 m >= n 的情况:
在这种情况下,促销活动便失去了意义,那么我们只需考虑第一天购买会比较便宜还是第二天购买会比较便宜即可.
m
第一种:在促销活动种尽可能地获利,然后剩下的部分考虑第一题便宜还是第二天便宜.
第二种:如果第一天购买商品价格较高,那么即便在促销活动种获利了,依然有可能不是最优解,所以我们可以选择把 n 个商品全部在第二天进行购买.
参考代码:
void solve() {
ll a, b, n, m;
cin >> a >> b >> n >> m;
if (m >= n)
cout << min(a, b) * n << '\n';
else
cout << min(n * b, (n / (m + 1)) * a * m +
(n - (n / (m + 1)) * (m + 1)) * min(a, b)) << '\n';
} //注意要开long long!
分析:
诈骗题吧,所有方案中峰值的数量和谷值的数量一定是相同的.然后中间需要一些点将峰和谷连起来,
因为是一个环,所以峰一定会先到谷然后再回来到另一个峰,所以最小的 n 一定就是2(x−y).最简单的构造方法就是只构造一个峰和一个谷.题目里保证了n的范围,所以无需担心2(x−y)太大.
参考代码:
void solve() {
int x, y;
std::cin >> x >> y;
std::cout << 2 * (x - y) << '\n';
for (int i = 0; i < x - y; i++)
std::cout << x - i << ' ';
for (int i = x - y; i < 2 * (x - y); i++)
std::cout << y + i - (x - y) << ' ';
std::cout << '\n';
}
void solve() {
int x, y;
std::cin >> x >> y;
std::vector ans;
for (int i = x; i > y; i--)
ans.push_back(i);
for (int i = y; i < x; i++)
ans.push_back(i);
std::cout << ans.size() << '\n';
for (int i = 0; i < ans.size(); i++)
std::cout << ans[i] << " \n"[i == ans.size() - 1];
}
分析:
对于当前的序列,如果第一个数/最后一个数恰好就是最大值/最小值,那么显然第一个数/最后一个数不可能作为端点,我们可以直接删除,直到第一个数/最后一个数既不是当前的最大值也不是当前的最小值,我们就找到了解.如果数列中所有数都被删除了则无解.
参考代码:
void solve() {
int n;
std::cin >> n;
std::deque q;
for (int i = 1; i <= n; i++) {
int x;
std::cin >> x;
q.push_back(x);
}
int l = 1, r = n;
int mx = n, mn = 1;
while (!q.empty()) {
if (q.back() == mx) {
q.pop_back();
mx--;
r--;
}
else if (q.back() == mn) {
q.pop_back();
mn++;
r--;
}
else if (q.front() == mx) {
q.pop_front();
mx--;
l++;
}
else if (q.front() == mn) {
q.pop_front();
mn++;
l++;
}
else
break;
}
if (q.empty())
std::cout << -1 << '\n';
else
std::cout << l << ' ' << r << '\n';
}
void solve() {
std::cin >> n;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
int l = 1, r = n, maxx = n, minx = 1;
while (l <= r) {
if (a[l] != maxx && a[l] != minx && a[r] != maxx && a[r] != minx)
break;
while (a[l] == maxx || a[l] == minx) {
if (a[l] == maxx) {
maxx--;
} else {
minx++;
}
l++;
}
while (a[r] == maxx || a[r] == minx) {
if (a[r] == maxx) {
maxx--;
} else {
minx++;
}
r--;
}
}
if (l < r)
std::cout << l << " " << r << "\n";
else
std::cout << -1 << "\n";
}
译文:
在冬天,莫斯科动物园的居民非常无聊,尤其是大猩猩。你决定娱乐他们,带了一个长度为n的排列p到动物园。
长度为n的排列是由n个从1到n的不同整数以任意顺序组成的数组。
大猩猩有他们自己的长度为n的排列q。他们建议你计算整数l,r(1≤l≤r≤n)对的数量,使MEX([pl,pl+1,…,pr])=MEX([ql,ql+1,…,qr])。
数列的MEX是数列中缺少的最小正整数。例如,MEX([1,3])=2, MEX([5])=1,MEX([3,1、2、6])= 4。
你不想拿自己的健康冒险,所以你也不敢拒绝大猩猩。
思路:
我们按照
mex
来统计答案.首先统计mex=1的区间,显然这个区间内不能包含 p 或 q 中的 1.
当前 mex 为 x ,显然我们必须要选择包含1∼x−1的区间.此外区间中还不能包含x.
假设在两 个序列中都选择1∼x−1之后的区间为 [ l , r ] .如果 p 或 q 中某个 x 在 [ l , r ] 中,显然mex是不可能为 x 的.
所以我们只需要考虑 p 和 q 中 x 的位置在 [l , r ] 之外的情况即可.这个条件相当于给左区间和右区间的延伸加上了一个限制,根据这个限制统计答案即可.
参考代码:
#include
using LL = long long;
const int maxn = 2e5 + 5;
int p[maxn], q[maxn];
int posp[maxn], posq[maxn];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
auto f = [](int x)
{
return 1LL * x * (x + 1) / 2;
};
int n;
std::cin >> n;
for (int i = 1; i <= n; i++)
std::cin >> p[i], posp[p[i]] = i;
for (int i = 1; i <= n; i++)
std::cin >> q[i], posq[q[i]] = i;
LL ans{};
int l = posp[1];
int r = posq[1];
if (l > r)
std::swap(l, r);
ans += f(l - 1) + f(n - r) + f(r - l - 1);
for (int i = 1; i <= n; i++) {
if ((posp[i + 1] < l || posp[i + 1] > r) && (posq[i + 1] < l || posq[i + 1] > r)) {
int L = 1, R = n;
if (i + 1 <= n) {
if (posp[i + 1] < l)
L = std::max(L, posp[i + 1] + 1);
if (posp[i + 1] > r)
R = std::min(R, posp[i + 1] - 1);
if (posq[i + 1] < l)
L = std::max(L, posq[i + 1] + 1);
if (posq[i + 1] > r)
R = std::min(R, posq[i + 1] - 1);
}
ans += 1LL * (l - L + 1) * (R - r + 1);
}
l = std::min(l, std::min(posp[i + 1], posq[i + 1]));
r = std::max(r, std::max(posp[i + 1], posq[i + 1]));
}
std::cout << ans;
return 0;
}