Codeforces Round 953 (Div.2)

传送门

A. Alice and Books

时间限制:1秒        空间限制:256MB        输入:标准输入        输出:标准输出

题目描述

Alice 有 n 本书。第 1 本书有 a_1 页,第 2 本书有 a_2 页,依此类推,第n本书有 a_n 页。Alice 做了以下操作:

她把所有的书分成两个非空的堆。因此,每本书最终都会被放在这两个堆中的一个。
Alice 在每个堆中选择编号最大的一本书来阅读。
Alice 非常喜欢阅读。帮助她找出通过将书分成两堆后,她可以阅读的最大总页数。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \le t \le 500),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的第一行包含一个整数 n (2 \le n \le 100),表示 Alice 拥有的书的数量。

每个测试用例的第二行包含 n 个整数 a_1, a_2, \cdots, a_n (1 \le a_i \le 10^9),表示每本书的页数。

输出格式

对于每个测试用例,输出一个整数,表示 Alice 可以阅读的最大页数。

样例输入

5\\ 2\\ 1 \hspace{0.5em} 1\\ 4\\ 2 \hspace{0.5em} 3 \hspace{0.5em} 3 \hspace{0.5em} 1\\ 5\\ 2 \hspace{0.5em} 2 \hspace{0.5em} 3 \hspace{0.5em} 2 \hspace{0.5em} 2\\ 2\\ 10 \hspace{0.5em} 3\\ 3\\ 1 \hspace{0.5em} 2 \hspace{0.5em} 3

样例输出

2\\ 4\\ 5\\ 13\\ 5

思路

无论怎么分堆,编号最大的书一定是会被阅读的;除此之外,还可以阅读剩余书本中页数最多的书

注释

在第一个测试用例中,Alice 可以把第 1 本书放在第一个堆里,把第 2 本书放在第二个堆里。然后她会阅读 a_1 + a_2 = 1 + 1 = 2 页。

在第二个测试用例中,Alice 可以把第 2 本书和第 3 本书放在第一个堆里,把第 1 本书和第 4 本书放在第二个堆里。然后她会从第一个堆中阅读页数最多的第 3 本书,从第二个堆中阅读页数最多的第 4 本书。然后她会阅读 a_3 + a_4 = 3 + 1 = 4 页。

代码

#include <bits/stdc++.h>
using namespace std;
int t, n, a[105];
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

int main() {
    t = read();
    while (t--) {
        n = read();
        for (int i = 1; i <= n; i++) a[i] = read();
        int ans = 0;
        for (int i = n - 1; i; i--) ans = max(ans, a[i]);
        ans += a[n];
        printf("%d\n", ans);
    }
    return 0;
}

B. New Bakery

时间限制:1秒        空间限制:256MB        输入:标准输入        输出:标准输出

题目描述

Bob 决定开一家面包店。在开业当天,他烤了 n 个可以出售的面包。面包的通常价格是 a 个硬币,但为了吸引顾客,Bob 组织了以下促销活动:

  • Bob 选择某个整数 k (0 \le k \le min(n, b))。
  • Bob 以修改后的价格出售前 k 个面包。在这种情况下,第 i 个 (1 \le i \le k) 售出的面包的价格是 b - i + 1 个硬币。
  • 剩下的 n - k 个面包以 a 个硬币的价格出售。

注意,k 可以等于 0。在这种情况下,Bob将以 a 个硬币的价格出售所有面包。

请帮助 Bob 确定通过出售所有 n 个面包可以获得的最大利润。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \le t \le 10^4),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的唯一一行包含三个整数 n , a 和 b (1 \le n,a,b \le 10^9),分别表示面包的数量、面包的通常价格和第一个以修改价格出售的面包的价格。

输出格式

对于每个测试用例,输出一个整数,表示Bob可以获得的最大利润。

样例输入

7\\ 4 \hspace{0.5em} 4 \hspace{0.5em} 5\\ 5 \hspace{0.5em} 5 \hspace{0.5em} 9\\ 10 \hspace{0.5em} 10 \hspace{0.5em} 5\\ 5 \hspace{0.5em} 5 \hspace{0.5em} 11\\ 1000000000 \hspace{0.5em} 1000000000 \hspace{0.5em} 1000000000\\ 1000000000 \hspace{0.5em} 1000000000 \hspace{0.5em} 1\\ 1000 \hspace{0.5em} 1 \hspace{0.5em} 1000

样例输出

17\\ 35\\ 100\\ 45\\ 1000000000000000000\\ 1000000000000000000\\ 500500

思路

只有当修改后的价格大于修改前的价格才进行价格修改,即

b - i + 1 \ge a \Rightarrow i \le b - a + 1

假设前 k 个面包需要修改的价格,为了使利润最大,我们要使 k = b - a + 1,但同时,k 也要在[0, n] 的范围之内,最后得出来的总价格为:

sum = k \times (b - i + 1) + (n - k) \times a \Rightarrow k(b + 1) - \frac{k(k + 1)}{2} + (n - k)a

注释

在第一个测试用例中,Bob 最优的选择是 k = 1。然后他将以 5 个硬币的价格卖出一个面包,并以每个 4 个硬币的通常价格卖出三个面包。这样利润将是 5 + 4 + 4 + 4 = 17 个硬币。

在第二个测试用例中,Bob 最优的选择是 k = 5。然后他将所有面包以修改后的价格卖出,获得利润 9 + 8 + 7 + 6 + 5 = 35 个硬币。

在第三个测试用例中,Bob 最优的选择是 k = 0。然后他将所有面包以通常的价格卖出,获得利润 10 \times 10 = 100 个硬币。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t, n, a, b;
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
signed main() {
    t = read();
    while (t--) {
        n = read(), a = read(), b = read();
        int k = min(n, b - a + 1), sum;
        if (k < 0) k = 0;
        sum = k * (b + 1) - k * (k + 1) / 2 + (n - k) * a;
        printf("%lld\n", sum);
    }
    return 0;
}

C. Manhattan Permutations

时间限制:2秒        空间限制:256MB        输入:标准输入        输出:标准输出

题目描述

我们称排列 p 的曼哈顿值为表达式 |p_1 - 1| + |p_2 - 2| + \cdots + |p_n - n| 的值。

例如,对于排列 [1, 2, 3],曼哈顿值是 |1 - 1| + |2 - 2| + |3 - 3| = 0;对于排列 [3, 1, 2],曼哈顿值是 |3 - 1| + |1 - 2| + |2 - 3| = 2 + 1 + 1 = 4。

给定整数 n 和 k。找到长度为 n 的一个排列 p,使其曼哈顿值等于 k,或者确定不存在这样的排列。

† 一个长度为 n 的排列是一个包含从 1 到 n 的 n 个不同整数的数组。例如, [2, 3, 1, 5, 4] 是一个排列,但 [1, 2, 2] 不是一个排列(2 在数组中出现了两次),并且 [1, 3, 4] 也不是一个排列(n = 3 但数组中有 4)。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \le t \le 10^4),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的唯一一行包含两个整数 n 和 k (1 \le n \le 2 \times 10^5, 0 \le k \le 10^{12}),分别表示排列的长度和所需的曼哈顿值。

保证所有测试用例中 n 的总和不超过 2 \times 10^5

输出格式

对于每个测试用例,如果没有合适的排列,输出 "No"。否则,第一行输出 "Yes",第二行输出 n 个不同的整数 p_1, p_2, \cdots, p_n (1 \le p_i \le n),表示一个合适的排列。

如果有多个解决方案,可以输出其中任何一个。

你可以以任何大小写形式输出答案(例如,"yEs", "yes", "Yes" 和 "YES" 都是正确答案)。

样例输入

8\\ 3 \hspace{0.5em} 4\\ 4 \hspace{0.5em} 5\\ 7 \hspace{0.5em} 0\\ 1 \hspace{0.5em} 1000000000000\\ 8 \hspace{0.5em} 14\\ 112 \hspace{0.5em} 777\\ 5 \hspace{0.5em} 12\\ 5 \hspace{0.5em} 2

样例输出

Yes\\ 3 \hspace{0.5em} 1 \hspace{0.5em} 2\\ No\\ Yes\\ 1 \hspace{0.5em} 2 \hspace{0.5em} 3 \hspace{0.5em} 4 \hspace{0.5em} 5 \hspace{0.5em} 6 \hspace{0.5em} 7\\ No\\ Yes\\ 8 \hspace{0.5em} 2 \hspace{0.5em} 3 \hspace{0.5em} 4 \hspace{0.5em} 5 \hspace{0.5em} 6 \hspace{0.5em} 1 \hspace{0.5em} 7\\ No\\ Yes\\ 5 \hspace{0.5em} 4 \hspace{0.5em} 3 \hspace{0.5em} 1 \hspace{0.5em} 2\\ Yes\\ 2 \hspace{0.5em} 1 \hspace{0.5em} 3 \hspace{0.5em} 4 \hspace{0.5em} 5

注释

在第一个测试用例中,排列 [3, 1, 2] 是合适的,其曼哈顿值为 |3 - 1| + |1 - 2| + |2 - 3| = 2 + 1 + 1 = 4。

在第二个测试用例中,可以证明长度为 4 的排列不存在其曼哈顿值为 5 的情况。

在第三个测试用例中,排列 [1, 2, 3, 4, 5, 6, 7] 是合适的,其曼哈顿值为 |1 - 1| + |2 - 2| + |3 - 3| + |4 - 4| + |5 - 5| + |6 - 6| + |7 - 7| = 0。

思路

  • 假设最初始的排列为 p_i = i,任意交换两个 p_i 的值,曼哈顿排列都为偶数。

证明:对于任意的 1 \le i < j \le n,交换 p_i 和 p_j 的值后,曼哈顿值就会变成

|p_i - i| + |p_j - j| = 2 \times |j - i| = 2 \times(j - i)

  • 将首尾对应位置的 p_i 的值全部交换之后,曼哈顿值一定是最大的,且最大的曼哈顿值为 

2 \times ((n - 1) + (n - 3) + \cdots + 1) = \frac{n^2}{2}

所以如果 k 是奇数或者 k > \frac{n^2}{2},不存在对应的曼哈顿排列,否则存在曼哈顿排列。

如果存在对应的曼哈顿排列,接下来我们再找出对应的曼哈顿排列。

只需要从头开始遍历,如果交换首尾对应元素后曼哈顿值小于 k,则进行交换;否则,如果现有曼哈顿值比 k 小 x,则交换第 i 位和第 i + \frac{x}{2} 位元素,并且结束遍历。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5 + 10;
int t, n, k, p[N];
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
signed main() {
    t = read();
    while (t--) {
        n = read(), k = read();
        if ((k & 1) || n * n / 2 < k) { puts("No"); continue; }
        puts("Yes");
		k /= 2;
        for (int i = 1; i <= n; i++) p[i] = i;
        for (int i = 1; k > 0; i++) {
            if (n + 1 - 2 * i <= k) {
                swap(p[i], p[n - i + 1]);
                k -= n - 2 * i + 1;
            } else {
				swap(p[i], p[i + k]);
				k = 0;
			}
        }
        for (int i = 1; i <= n; i++) printf("%d ", p[i]);
		putchar('\n');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值