AT_jag2018summer_day2_d Knapsack And Queries
题目描述
首先,给定一个正整数 $MOD$。
你有一个背包,初始时为空。
你需要执行 $Q$ 次查询。
- 在每次查询中,你需要先执行一个 `ADD` 或 `REMOVE` 操作,然后执行一个 `FIND` 操作。
- `ADD` 操作:给定正整数 $w, v$。将一个重量为 $w$、价值为 $v$ 的饼干放入背包。
- `REMOVE` 操作:从背包中取出重量最小的饼干并吃掉。
- `FIND` 操作:给定非负整数 $l, r$。你需要回答以下问题:
- 是否可以从背包中选择一些饼干,使得所选饼干的总重量 $X$ 满足 $l \leq (X\ {\rm mod}\ MOD) \leq r$?
- 如果不能,输出 `-1`。
- 否则,输出所选饼干的最大总价值。
输入格式
输入格式如下:
> $MOD$\
> $Q$\
> $t'_1$ $w'_1$ $v'_1$ $l'_1$ $r'_1$\
> $t'_2$ $w'_2$ $v'_2$ $l'_2$ $r'_2$\
> :\
> $t'_Q$ $w'_Q$ $v'_Q$ $l'_Q$ $r'_Q$
- $0 \leq t'_i, w'_i, v'_i, l'_i, r'_1 \leq 2^{30} - 1$
- 查询是**在线查询**。你可以通过解码 $t'_i, w'_i, v'_i, l'_i, r'_i$ 来获得 $t_i, w_i, v_i, l_i, r_i$。
- $t_i$ 表示查询类型。
- 当 $t_i = 1$ 时,执行 `ADD` 操作和 `FIND` 操作。
- 当 $t_i = 2$ 时,执行 `REMOVE` 操作和 `FIND` 操作。
- 当 $t_i = 2$ 时,你可以假定 $w_i=0$ 且 $v_i = 0$。
### 加密方式
我们提供了使用 C++11 (或更高版本)、Java、D、C# 编写的解密代码。
请使用 `class Crypto` 进行解密,`class Crypto` 的代码如下所示。
::::info[c++]
```cpp
#include //uint8_t, uint32_t
class Crypto {
public:
Crypto() {
sm = cnt = 0;
seed();
}
int decode(int z) {
z ^= next();
z ^= (next() > (i * 8));
}
for (int i = 0; i < 256; i++) {
data[i] = i;
}
I = J = 0;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + data[i] + key[i%8]) % 256;
swap_data(i, j);
}
}
uint8_t next() {
I = (I+1) % 256;
J = (J + data[I]) % 256;
swap_data(I, J);
return data[(data[I] + data[J]) % 256];
}
};
```
::::
::::info[Java]
```java
public class Main {
public static class Crypto {
public Crypto() {
sm = cnt = 0;
seed();
}
public int decode(int z) {
z ^= next();
z ^= (next() (i * 8));
}
for (int i = 0; i < 4; i++) {
key[i+4] = (byte)(cnt >>> (i * 8));
}
for (int i = 0; i < 256; i++) {
data[i] = (byte)i;
}
I = J = 0;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + asUint(data[i]) + asUint(key[i % 8])) % 256;
byte tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
}
int next() {
I = (I+1) % 256;
J = (J+asUint(data[I])) % 256;
byte tmp = data[I];
data[I] = data[J];
data[J] = tmp;
return asUint(data[(asUint(data[I]) + asUint(data[J])) % 256]);
}
}
public static void main(String[] args) {
...
}
}
```
::::
::::info[D]
```d
class Crypto {
public:
this() {
sm = cnt = 0;
seed();
}
int decode(int z) {
z ^= next();
z ^= (next() > (i*8));
}
foreach (i; 0..256) {
data[i] = cast(ubyte)(i);
}
I = J = 0;
int j = 0;
foreach (int i; 0..256) {
j = (j + data[i] + key[i%8]) % 256;
swap(data[i], data[j]);
}
}
ubyte next() {
I = (I+1) % 256;
J = (J + data[I]) % 256;
swap(data[I], data[J]);
return data[(data[I] + data[J]) % 256];
}
}
```
::::
::::info[C#]
```Csharp
class Crypto {
public Crypto() {
sm = cnt = 0;
data = new byte[256];
seed();
}
public int decode(int z) {
z ^= next();
z ^= (next() > (i*8));
}
for (int i = 0; i < 256; i++) {
data[i] = (byte)i;
}
I = J = 0;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + data[i] + key[i%8]) % 256;
byte tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
}
byte next() {
I = (I+1) % 256;
J = (J + data[I]) % 256;
byte tmp = data[I];
data[I] = data[J];
data[J] = tmp;
return data[(data[I] + data[J]) % 256];
}
}
```
::::
解密过程如下:
- 首先,你需要创建一个 `class Crypto` 的实例。
- 对于每次查询,
- 依次调用 `decode` 函数,传入 $t', w', v', l', r'$,其返回值分别为 $t, w, v, l, r$。
- 使用 `FIND` 操作的结果调用 `query` 函数。
示例代码如下所示。
::::info[C++]
```cpp
#include
#include
#include //uint8_t, uint32_t
class Crypto {
...
};
int main() {
int MOD, Q;
scanf("%d %d", &MOD, &Q);
Crypto c;
for (int i = 0; i < Q; i++) {
int t, w, v, l, r;
scanf("%d %d %d %d %d", &t, &w, &v, &l, &r);
t = c.decode(t);
w = c.decode(w);
v = c.decode(v);
l = c.decode(l);
r = c.decode(r);
if (t == 1) {
(add candy(w, v))
} else {
(delete candy)
}
long long ans = (answer for query(l, r));
c.query(ans);
printf("%lld\n", ans);
}
}
```
::::
::::info[Java]
```Java
import java.io.PrintWriter;
import java.util.Scanner;
public class Main {
public static class Crypto {
...
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in); //!!warning!! : Java's scanner is slow
PrintWriter out = new PrintWriter(System.out);
int MOD = in.nextInt();
int Q = in.nextInt();
Crypto c = new Crypto();
for (int i = 0; i < Q; i++) {
int t, w, v, l, r;
t = c.decode(in.nextInt());
w = c.decode(in.nextInt());
v = c.decode(in.nextInt());
l = c.decode(in.nextInt());
r = c.decode(in.nextInt());
if (t == 1) {
(add candy(w, v))
} else {
(delete candy)
}
long ans = (answer for query(l, r));
c.query(ans);
out.println(ans);
}
out.flush();
}
}
```
::::
::::info[D]
```d
class Crypto {
...
}
int main() {
import std.stdio;
int MOD, Q;
readf("%d\n%d\n", &MOD, &Q);
Crypto c = new Crypto();
foreach (i; 0..Q) {
int t, w, v, l, r;
readf("%d %d %d %d %d\n", &t, &w, &v, &l, &r);
t = c.decode(t);
w = c.decode(w);
v = c.decode(v);
l = c.decode(l);
r = c.decode(r);
if (t == 1) {
(add candy(w, v))
} else {
(delete candy)
}
long ans = (answer for query(l, r));
c.query(ans);
writeln(ans);
}
return 0;
}
```
::::
::::info[C#]
```Csharp
using System;
using System.IO;
class Crypto {
...
}
class Myon {
static void Main() {
var writer = new StreamWriter(Console.OpenStandardOutput()){AutoFlush = false}; //fast writer
Console.SetOut(writer);
int MOD = int.Parse(Console.ReadLine());
int Q = int.Parse(Console.ReadLine());
Crypto c = new Crypto();
for (int i = 0; i < Q; i++) {
var inputs = Console.ReadLine().Split(' ');
int t, w, v, l, r;
t = c.decode(int.Parse(inputs[0]));
w = c.decode(int.Parse(inputs[1]));
v = c.decode(int.Parse(inputs[2]));
l = c.decode(int.Parse(inputs[3]));
r = c.decode(int.Parse(inputs[4]));
if (t == 1) {
(add candy(w, v))
} else {
(delete candy)
}
long ans = (answer for query(l, r));
c.query(ans);
Console.WriteLine(ans);
}
Console.Out.Flush();
}
}
```
::::
备注:
- 你不需要寻找此加密方法的漏洞。
- 当 $Q = 100,000$ 时,`class Crypto` 的运行时间最多约为 $200$ 毫秒。
输出格式
输出 `FIND` 操作的结果,每行一个。
说明/提示
**【样例解释1】**
解码此输入的结果如下。
```
10
7
1 5 10 5 5
2 0 0 0 9
1 7 10 2 4
1 12 11 9 9
2 0 0 1 1
1 22 10 2 3
1 32 100 4 4
```
| 背包 | 选定的饼干 |
|:----:|:---------:|
| (w, v) = {(5, 10)} | {(5, 10)} |
| {} | {} |
| {(7, 10)} | -1 |
| {(7, 10), (12, 11)} | {(7, 10), (12, 11)} |
| {(12, 11)} | -1 |
| {(12, 11), (22, 10)} | {(12, 11)} |
| {(12, 11), (22, 10), (32, 100)} | {(12, 11), (32, 100)} |
**【样例解释2】**
解码此输入的结果如下。
```
7
20
1 5 44 0 1
1 11 90 0 3
2 0 0 3 4
1 18 68 1 6
1 25 32 2 3
1 31 22 2 3
1 32 26 1 5
1 36 31 3 6
2 0 0 2 5
1 43 10 3 6
2 0 0 5 6
2 0 0 3 4
2 0 0 2 4
2 0 0 1 5
2 0 0 3 5
1 49 48 0 4
2 0 0 1 5
1 50 36 0 6
1 56 48 3 5
1 59 17 3 5
```
**【数据范围】**
- $2 \leq MOD \leq 500$
- $1 \leq Q \leq 100,000$
- $1 \leq w_i, v_i \leq 10^9$
- $0 \leq l_i \leq r_i \leq MOD-1$
- 每次 `ADD` 操作添加的饼干,其重量都严格大于此前任何 `ADD` 操作所添加的饼干。
- 执行 `REMOVE` 操作时,背包不为空。
- 查询是 **在线查询**。查询将按后续描述进行加密。