A. Catch the Coin

不难发现,只有硬币的 \(y\) 坐标大于等于 \(-1\) 时,\(Monocarp\) 才有可能收集到该硬币

code:

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
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
int x, y;

signed main()
{
cin >> T;
while (T--)
{
cin >> x >> y;

if (y >= -1) cout << "YES" << endl;
else cout << "NO" << endl;
}

return 0;
}

B. Substring and Subsequence

转化题意,只需计算出 \(b\) 中 能够作为 \(a\) 的子序列的最长子串 \(k\),包含 $ $ 作为子字符串和 \(b\) 作为子序列的字符串的最小可能长度即为 \(a.size()+k.size()\)

code:

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
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
string a, b;

signed main()
{
cin >> T;
while (T--)
{
cin >> a >> b;

int res = 0;
for (int i = 0; i < b.size(); i++)
for (int j = 0, k = i; j < a.size() && k < b.size(); j++)
{
if (a[j] == b[k]) k++;
if (j == a.size() - 1 || k == b.size()) res = max(res, k - i);
}

cout << a.size() + b.size() - res << endl;
}

return 0;
}

C. Two Movies

对于 \(a[i]\)\(b[i]\),它们的组合有以下几种情况:

  • \(a[i]=1,b[i]\neq1\),该观众给第一步电影留下评价最佳
  • \(a[i]\neq1,b[i]=1\),该观众给第二部电影留下评价最佳
  • \(a[i]=1,b[i]=1\),该观众留下的评价一定是正面评价,先不做选择,记为 \(cnt++\)
  • \(a[i]=-1,b[i]=-1\),该观众留下的评价一定是负面评价,先不做选择,记为 \(cnt--\)

\(cnt\) 即为可以让两个电影的评价增加的值

每个观众都操作过后,为了使两部电影的最低评分最大,需要使用 \(cnt\) 对两部电影的评分进行操作,使得两个评分最接近,这时两部电影的最低评分最大

code:

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
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
int n;
int a[N], b[N];

signed main()
{
cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];

int r1 = 0, r2 = 0, c1 = 0, c2 = 0;
for (int i = 0; i < n; i++)
{
if (a[i] == 1 && b[i] == -1) r1++;
if (a[i] == -1 && b[i] == 1) r2++;
if (a[i] == 1 && b[i] == 1) c1++;
if (a[i] == -1 && b[i] == -1) c2++;
}

for (int i = 0; i < c1; i++)
{
if (r1 <= r2) r1++;
else r2++;
}
for (int i = 0; i < c2; i++)
{
if (r1 >= r2) r1--;
else r2--;
}

cout << min(r1, r2) << endl;
}

return 0;
}

D. Smithing Skill

最优策略显然是制造完一件武器后立刻熔化,每操作一次贡献 \(+2\)

\(best[i]\) 表示为用最多 \(i\) 块金属锭 制作并熔化一个武器所消耗金属锭的最小值,可以用双指针维护

\(f[i]\) 为 用 \(i\) 块金属锭能够获得的最大经验值,考虑用 \(dp\),递推公式为: \(f[i]=f[i-best[i]]+2\)

接下来遍历数组 \(c\)

  • \(c[i]<=1e6\),对答案的贡献就是 \(f[c[i]]\)
  • \(c[i]>1e6\),对于大于\(1e6\)的部分,不难发现,每制作并熔化一个武器所消耗的金属锭最小值为 \(best[1e6]\),因此,可以先处理大于\(1e6\) 的部分对答案的贡献,为 \((\frac{c[i]-1e6-1}{best[1e6]}+1)\times 2\),再使用\(f\)数组处理小于等于 \(1e6\) 的部分即可

code:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>

#define int long long
#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1000010;

int T;
int n, m;
int a[N], b[N], c[N];
int best[N], f[N];

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);

cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
for (int i = 0; i < m; i++) cin >> c[i];

map<int, int> p;
for (int i = 0; i < n; i++)
{
if (!p.count(a[i])) p[a[i]] = 1e9;
p[a[i]] = min(p[a[i]], a[i] - b[i]);
}

int last = 0;
auto it = p.begin();
for (int i = 1; i < N; i++)
{
if (it != p.end() && it -> x == i)
{
if (it -> y < last || !last) last = it -> y;
it++;
}
best[i] = last;
}
for (int i = 1; i < N; i++)
if (best[i]) f[i] = f[i - best[i]] + 2;

int res = 0;
for (int i = 0; i < m; i++)
{
if (c[i] >= N)
{
res += ((c[i] - N) / last + 1) * 2;
c[i] = N + (c[i] - N) % last - last;
}
res += f[c[i]];
}

cout << res << endl;

return 0;
}

E. Distance to Different

通过观察发现

当序列中间有两个相同元素相邻时,例如 \(1,2,2,3\),将这两个相同的元素替换成两个不同的元素不影响答案,只有两个相同元素出现在头或尾,才不能被替换,因此,序列中间连续相同的元素块长度大于 \(2\) 时,对答案才有意义

对于序列中间每个块,里面的值是什么并不影响答案,因此,可以把题意转化为把数组分为 \(k\) 块,并且中间连续相同元素块长度大于 \(2\),可以用 \(dp\) 来完成操作

\(f[i][j]\) 为 前 \(i\) 个元素,分割成 \(j\) 个块的方案数,\(sum[i][j]\),为不超过 \(i\) 个元素,分割成 \(j\) 块的方案数

code:

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
#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 200010, mod = 998244353;

int n, k;
int f[N][15], sum[15];

signed main()
{
cin >> n >> k;

sum[0] = f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= k; j++)
{
f[i][min(j + 1, k)] = (f[i][min(j + 1, k)] + sum[j]) % mod;
if (i > 2 && i < n) f[i][min(j + 1, k)] = (f[i][min(j + 1, k)] - f[i - 2][j] + mod) % mod;
}
for (int j = 0; j <= k; j++) sum[j] = (sum[j] + f[i][j]) % mod;
}

cout << f[n][k] << endl;

return 0;
}