Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • codeforses 题解

  • atcoder 题解

  • XCPC 题解

  • 校训练赛题解

  • 牛客题解

    • 2024牛课暑期多校训练营5 题解
      • B - 珑
        • Question
        • Solution
        • Code
      • H - 入
        • Question
        • Solution
        • Code
      • L - 知
        • Question
        • Solution
        • Code
    • 2024牛客暑期多校训练营6 题解
    • 2024牛客暑期多校训练营7 题解
    • 2024牛客暑期多校训练营8 题解
    • 2024牛客暑期多校训练营9 题解
  • 蓝桥杯题解

  • 典题选讲

  • 杂题

  • 算法题解
  • 牛客题解
martian148
2025-04-02
目录

2024牛课暑期多校训练营5 题解

# B - 珑

# Question

你需要用若干个 1×2 或 2×1 的多米诺骨牌覆盖一个 n×mn \times mn×m 的矩形。每个位置必须恰好被覆盖一次,且多米诺骨牌不得超出矩形范围。

此外,可能存在两种类型的约束:

  1. 多米诺骨牌的短边不能相邻,即没有两个多米诺骨牌可以共享长度为 1 的边。
  2. 多米诺骨牌的长边不能相邻,即没有两个多米诺骨牌可以共享长度为 2 的边(即使它们只共享一条边)。

有 TTT 个查询,每个查询给出 n,m,a,bn, m, a, bn,m,a,b,分别代表矩形的大小以及两种约束是否存在。当 aaa 为 0 时,第一种约束存在;当 aaa 为 1 时,第一种约束不存在。当 bbb 为 0 时,第二种约束存在;当 bbb 为 1 时,第二种约束不存在。对于每个查询,你需要判断是否有方法可以覆盖整个矩形。

# Solution

可以手玩一下,一共是四种情况

显然,n×mn\times mn×m 为奇数的时候,肯定无解

  1. a=b=1a=b=1a=b=1

用 1×21\times 21×2 的随便放就好了

  1. a=1,b=0a=1,b=0a=1,b=0

也就是长边不能相邻

image-20240807112847692

只有这种情况能实现

  1. a=0,b=1a=0, b=1a=0,b=1

也就是断边不能相邻,我们可以组成一个基本单元

image-20240807112953943

于是,我们就组成了一个 2×32\times 32×3 的基本单元,所以偶数边就满足了,奇数边可能模 333 为 111,或为 222,我们已经有 1×21\times 21×2 和 2×22\times 22×2 的方法,所以 a=0,b=1a=0,b=1a=0,b=1 的都能放

  1. a=b=0a=b=0a=b=0

只有 1×21\times 21×2 能满足

# Code

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n, m, a, b; cin >> n >> m >> a >> b; a ^= 1, b ^= 1;
    if ((n == 1 && m == 2) || (n == 2 && m == 1)) {
        cout << "Yes" << '\n';
        return;
    }
    if ((a == 0 && b == 0) || (a == 1 && b == 0)) {
        if ((a == 1 && b == 0) && (n == 1 || m == 1)) {
            cout << "No" << '\n';
            return;
        }
        if (n % 2 == 0 && m % 2 == 0) cout << "Yes" << '\n';
        else if (n % 2 == 1 && m % 2 == 1) cout << "No" << '\n';
        else cout << "Yes" << '\n';
    }
    else if ((a == 1 && b == 1)) {
        if ((n == 1 && m == 2) || (n == 2 && m == 1)) cout << "Yes" << '\n';
        else cout << "No" << '\n';
    }
    else if (a == 0 && b == 1) {
        if ((n == 1 && m % 2 == 0) || (m == 1 && n % 2 == 0)) cout << "Yes" << '\n';
        else cout << "No" << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--) solve();
    return 0;
}
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

# H - 入

# Question

有一个无向图,其中每个顶点都有唯一的权重aia_iai​。

最初,一颗棋子被放在一个顶点上。每走一步,棋子都会移动到权重最小的相邻顶点。如果所有相邻顶点的权重都大于当前顶点,则立即停止移动。

您可以为顶点自由分配权重 aia_iai​(确保它们都是唯一的),并选择棋子的初始位置。您的目标是使棋子访问的顶点数最大化。

  • n≤40n\le 40n≤40

# Solution

由于 nnn 非常小,所以我们考虑暴力 DFS

每次 DFS 到一个点,我们就把这个点从图中删掉(具体做法是用 vis 数组标记),然后继续 DFS,在回溯时,把 vis 标记去掉

# Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 45;

vector<int> g[MAXN];
int vis[MAXN];

int dfs(int u) {
    vector<int> p;
    for (auto v : g[u]) {
        if (vis[v]) continue;
        vis[v] = u;
        p.push_back(v);
    }
    int res = 1;
    for (auto v : p)
        res = max(res, dfs(v) + 1);
    for (auto v : p)
        if (vis[v] == u) vis[v] = 0;
    return res;
}


int main() {
    ios::sync_with_stdio(false);
    int n, m; cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v); g[v].push_back(u);
    }
    int res = 0;
    for (int i = 1; i <= n; i++) {
        vis[i] = i;
        res = max(res, dfs(i));
        vis[i] = 0;
    }
    cout << res << '\n';
    return 0;
}
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

# L - 知

# Question

游戏由 nnn个关卡组成,你事先知道通过每个关卡的概率,即 a1100,a2100,⋯,an100\frac{a_1}{100}, \frac{a_2}{100}, \cdots, \frac{a_n}{100}100a1​​,100a2​​,⋯,100an​​,其中 aia_iai​是非负整数。

您可以执行任意数量的操作。每个操作都允许您选择一个索引 i(1≤i<n,ai+1>0,ai<100)i \, (1 \leq i < n, a_{i+1} > 0, a_{i} < 100)i(1≤i<n,ai+1​>0,ai​<100),并将ai+1a_{i+1}ai+1​递减一个,同时将aia_{i}ai​递增一个。

执行操作后,开始游戏,目标是最大限度地提高通过所有关卡的概率。

输出最大概率乘以 100n100^n100n,模数为 998244353998244353998244353。

1≤T,n,ai≤1001 \le T, n, a_i \le 1001≤T,n,ai​≤100.

# Solution

我们的想法,肯定是让 pip_ipi​ 尽量均匀

但是由于我们只能让后面一个变小,然后前面一个变大

所以暴力的把,如果 ai+1>aia_{i+1}>a_iai+1​>ai​ 那么,让 cai+1−1,ai+1a_{i+1}-1,a_i+1ai+1​−1,ai​+1

暴力跑可能超时,所以,我们开一个 set<int> 记录下后面所有数,如果当前的 aia_iai​ 小于 set 中最小的数,那么把 set 中最大的数 −1-1−1,把 ai+1a_i+1ai​+1

# Code

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll TT = 998244353;

void solve() {
    int n; cin >> n;
    ll sum = 0;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    multiset<int> s;
    for (int i = n; i >= 1; i--) {
        while (true) {
            if ((!s.empty()) && a[i] < *s.rbegin()) {
                auto it = s.end(); --it;
                int x = *it;
                s.erase(it); x -= 1; a[i] += 1;
                s.insert(x);
            }
            else {
                s.insert(a[i]);
                break;
            }
        }
    }
    ll res = 1;
    for (auto x : s) res = res * x % TT;
    cout << res << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--) solve();
    return 0;
}
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
上次更新: 2025/04/08, 18:03:31
Sakura Tears training 5 题解
2024牛客暑期多校训练营6 题解

← Sakura Tears training 5 题解 2024牛客暑期多校训练营6 题解→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式