原题链接

B. 青蛙的诅咒

每次测试的时间限制:1 秒

每次测试的内存限制:256 兆字节

在一条无限数轴上,点 00 处坐着一只青蛙。经过多年的冥想,青蛙掌握了 nn 种独特类型的魔法跳跃。第 ii 种跳跃允许它向前跳跃不超过 aia_i 个单位。换句话说,如果它在整数点 kk,跳跃后它可以落在从 kkk+aik + a_i 的任意整数点。

但是魔法总是有代价的;它被诅咒了。在第 ii 种跳跃的每第 bib_i 次尝试之前(即在使用第 ii 种跳跃的第 bib_i 次、第 2bi2b_i 次、第 3bi3b_i 次等尝试中),青蛙会向后退 cic_i 个单位!换句话说,如果它在点 kk,它首先会发现自己在点 kcik - c_i,跳跃之后,它可以落在从 kcik - c_ikci+aik - c_i + a_i 的任意整数点。

青蛙的目标是到达数字为 xx 的点,同时使用跳跃并使回滚次数最少。帮助青蛙 —— 找到它在通往目标的路上必须忍受的最小回滚次数,或者确定它无法到达点 xx

输入

每个测试包含多个测试用例。第一行包含测试用例的数量 tt (1t1041 \le t \le 10^4)。测试用例的描述如下。

每个测试用例的第一行包含 2 个整数 nnxx (1n105,1x10181 \le n \le 10^5, 1 \le x \le 10^{18}) —— 青蛙可以进行的跳跃类型的数量及其最终目标。

接下来的 nn 行提供了跳跃类型的描述;第 ii 行包含 3 个整数 ai,bi,a_i, b_i,cic_i (1ai,bi,ci1061 \le a_i, b_i, c_i \le 10^6)。

保证所有测试用例的 nn 之和不超过 10510^5

输出

对于每个测试用例,如果青蛙能到达点 xx,找出它必须忍受的最小回滚次数。如果它不能到达点 xx,输出 1-1

示例

input (输入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
6
1 1
3 3 3
1 7
4 2 5
2 4
1 2 3
2 2 4
5 8
12 1 11
10 1 4
1 1 3
1 2 5
2 1 7
1 1000000000000000000
1000000 4 654321
1 10
2 2 1

output (输出)

1
2
3
4
5
6
0
1
-1
2
298892990032
3

在第一个测试用例中,青蛙可以向前跳跃 11 个单位并将在点 11 结束。因此,答案是 00

在第三个测试用例中,可以证明青蛙无法到达点 44

在第四个测试用例中,青蛙可以到达点 88,例如,如下所示:使用第 11 种类型跳跃 1212,使用第 44 种类型跳跃 11,使用第 22 种类型跳跃 1010。那么它将依次位于以下点 0(rollback)1112(rollback)280 \to (\textbf{rollback}) -11 \to 1 \to 2 \to (\textbf{rollback}) -2 \to 8

在第六个测试用例中,青蛙可以到达点 1010,例如,如下所示:跳跃 66 次使用类型 2211 次使用类型 11。那么它将依次位于以下点 02(rollback)135(rollback)468(rollback)79100 \to 2 \to (\textbf{rollback})1 \to 3 \to 5 \to (\textbf{rollback})4 \to 6 \to 8 \to (\textbf{rollback})7 \to 9 \to 10

题面解读

题目链接

一只在数轴 00 点的青蛙。跳到坐标 x\ge x 的位置。求最小的回滚次数。

解题思路

首先贪心的利用所有青蛙的安全跳跃距离$$\ \sum_{i=1}^{n} a_i \times (b_i - 1)$$如果这个总和 x\ge x,那么回滚次数就是 0

如果所有安全距离总和还不够,就必须开始回滚。贪心的挑选每次回滚距离最大的魔法max(ci+ai+ai(bi1))\max(-c_i + a_i + a_i(b_i - 1))那么,需要回滚的次数就是还需要跳的距离最大回滚距离\lceil \frac{\text{还需要跳的距离}}{\text{最大回滚距离}} \rceil

题解代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
typedef long long ls;
int t;

ls cal(ls a, ls b, ls c){
return (b-1)*a+a-c;
}

void solve(){
ls n, x;
ls a, b, c;
ls maxn = -2e18;
cin >> n >> x;
ls sum = 0;
for (int i = 1; i <= n; i++){
cin >> a >> b >> c;
sum += (b-1) * a;
if (cal(a,b,c) > maxn)
maxn = cal(a, b, c);
}
if (sum >= x){
cout << 0 << endl;
return;
}
if (maxn <= 0){
cout << -1 << endl;
return;
}
cout << (x - sum + maxn - 1) / maxn << endl;
}

int main(){
cin >> t;
while (t--) solve();
}

注:算法竞赛中向上取整ceil(a,b)更喜欢用(a + b - 1) / b来代替

C1. 异或便利 (简单版)

每次测试的时间限制:2 秒

每次测试的内存限制:256 兆字节

这是问题的简单版本。两个版本之间的区别在于,在此版本中,2in12 \le i \le n - 1。请注意,困难版本的正确解决方案不一定是简单版本的正确解决方案。

给定一个自然数 nn。找到一个长度为 nn 的排列* pp,使得对于每个 ii (2in12 \le i \le n - 1),存在一个 jj (ijni \le j \le n) 满足 pi=pjip_i = p_j \oplus i ^{\dagger}

可以证明,在问题的约束下,至少存在一个合适的排列 pp


* 长度为 nn 的排列是一个包含从 11nnnn 个不同整数的数组,顺序任意。例如,[2,3,1,5,4][2,3,1,5,4] 是一个排列,但 [1,2,2][1,2,2] 不是一个排列 (22 在数组中出现两次),[1,3,4][1,3,4] 也不是一个排列 (n=3n=3 但数组中有 44)。

\dagger \oplus 表示按位异或运算

输入

每个测试包含多个测试用例。第一行包含测试用例的数量 tt (1t1041 \le t \le 10^4)。测试用例的描述如下。

每个测试用例的唯一一行包含一个整数 nn (3n21053 \le n \le 2 \cdot 10^5) —— 排列的长度。

保证所有测试用例的 nn 之和不超过 21052 \cdot 10^5

输出

对于每个测试用例,输出 nn 个整数 p1,p2,,pnp_1, p_2, \dots, p_n —— 即排列 pp

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

示例

input (输入)

1
2
3
2
3
6

output (输出)

1
2
2 1 3
3 6 2 5 1 4

在第一个测试用例中,排列 p=[2,1,3]p = [2, 1, 3] 是合适的,因为 p2=1p_2 = 1p32=1p_3 \oplus 2 = 1

在第二个测试用例中,排列 p=[3,6,2,5,1,4]p = [3, 6, 2, 5, 1, 4] 是合适的,因为:

  • p2=6=42=p62p_2 = 6 = 4 \oplus 2 = p_6 \oplus 2
  • p3=2=13=p53p_3 = 2 = 1 \oplus 3 = p_5 \oplus 3
  • p4=5=14=p54p_4 = 5 = 1 \oplus 4 = p_5 \oplus 4
  • p5=1=45=p65p_5 = 1 = 4 \oplus 5 = p_6 \oplus 5

题面解读

题目链接

构造一个长度为 nn 的排列 pp(包含 11nn 不重复)。

对于中间的每一个位置 ii (2in12 \le i \le n-1),必须在它右边(下标 jij \ge i)找到一个搭档 pjp_j,满足:

pi=pji    pipj=ip_i = p_j \oplus i \quad \iff \quad p_i \oplus p_j = i

解题思路

为了让回滚次数最少,逻辑非常直观:先把免费的用完,不够再付费。

假设我们把 11 放在非常靠后的位置,比如 pn=1p_{n} = 1。 那么对于前面的所有 ii2in12 \le i \le n-1),我们只需要让 pipn=ip_i \oplus p_{n} = i,即 pi1=ip_i \oplus 1 = i。 这推导出:pi=i1p_i = i \oplus 1。填完了 p2p_2pnp_n,剩下的那个没用过的数字就是 p1p_1,可以根据奇偶性来求出。

性质1: A=BC    AB=CA = B \oplus C \iff A \oplus B = C

性质2:x1x \oplus 1 的作用是翻转 xx​ 二进制的最后一位。它的作用不是随机改变数字,而是成对交换相邻的偶数和奇数。

232 \leftrightarrow 321=32 \oplus 1 = 3, 31=23 \oplus 1 = 2

454 \leftrightarrow 5

676 \leftrightarrow 7

2k2k+12k \leftrightarrow 2k+1

题解代码

理解后代码其实非常好写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;

int t;

void solve(){
int n; cin >> n;
vector<int> a(n+1);
a[n] = 1;
for (int i = 2; i <= n - 1; i++)
a[i] = i ^ 1;
a[1] = n % 2 == 1 ? n - 1 : n;
for (int i = 1; i <= n; i++)
cout << a[i] << ' ';
cout << endl;
}

int main(){
cin >> t;
while (t--) solve();
}

注:官方题解把1放在pn1p_{n-1}其实是一种习惯,同时也方便下面一道困难题题解。