比赛链接
A. 在ACM里打ACM
Solution
模拟题意即可,需要记录的信息是:总的罚时 p e n pen pen,总共 a c ac ac 的题目数量 a c c acc acc,对 13 13 13 个题都记录两个数,一个是 f a i l fail fail 表示非 a c ac ac 提交的额外罚时,一个是 i s A c isAc isAc 表示这题是否 a c ac ac,因为可能出现过了某个题还继续交的情况,这种情况应该忽略任何这道题的提交。
C++ Code
#include <bits/stdc++.h>
using i64 = int64_t;
using u64 = uint64_t;
using f64 = double_t;
using i128 = __int128_t;
struct Team {
std::string s;
int acc = 0;
int pen = 0;
std::array<int, 13> fail{
};
std::array<int, 13> isAc{
};
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(12);
int n;
std::cin >> n;
std::vector<Team> team;
std::map<std::string, int> mp;
int m = 0;
for (int i = 0; i < n; i++) {
std::string s;
char p;
int ac;
int t;
std::cin >> s >> p >> ac >> t;
if (!mp.contains(s)) {
mp[s] = m++;
team.push_back({
s});
}
p -= 'A';
int id = mp[s];
if (ac == 0) {
if (!team[id].isAc[p]) {
team[id].fail[p] += 20;
}
continue;
}
if (!team[id].isAc[p]) {
team[id].acc++;
team[id].isAc[p] = 1;
team[id].pen += t + team[id].fail[p];
}
}
std::ranges::sort(team, [&](const auto &x, const auto &y) {
if (x.acc != y.acc) {
return x.acc > y.acc;
}
return x.pen < y.pen;
});
std::cout << team[0].s << " " << team[0].acc << " " << team[0].pen << "\n";
return 0;
}
B. 逆=辶+屰
题目大意
给定一个数字 n n n 和 q q q 个询问,每个询问给出两个正整数 k , r k,\ r k, r,要求回答:
- 是否存在 k k k 个 不同正整数 c 1 , c 2 , ⋯ , c k c_1, \ c_2, \ \cdots, \ c_k c1, c2, ⋯, ck满足 n ≡ r ( m o d c i ) , i ∈ [ 1 , k ] n \equiv r \pmod{c_i}, \ i \in [1, k] n≡r(modci), i∈[1,k]。
数据范围
- 1 ⩽ n ⩽ 1 0 6 1 \leqslant n \leqslant 10^6 1⩽n⩽106;
- 1 ⩽ q ⩽ 1 0 4 1 \leqslant q \leqslant 10^4 1⩽q⩽104;
- 1 ⩽ k ⩽ 1 0 6 1 \leqslant k \leqslant 10^6 1⩽k⩽106;
- 0 ⩽ r < n 0 \leqslant r < n 0⩽r<n;
Solution1
由同余的性质,得到 ( n − r ) ≡ 0 ( m o d c i ) , i ∈ [ 1 , k ] (n - r) \equiv 0 \pmod{c_i}, \ i \in [1, k] (n−r)≡0(modci), i∈[1,k],也就是说 ∀ c i \forall c_i ∀ci 都是 n − r n - r n−r 的因数。
所以我们只要对 n − r n - r n−r 进行质因数分解,然后计算所有 > r > r >r 的因数有多少即可。
要保证 > r > r >r 是因为, r r r 是模 c i c_i ci 后的结果,说明 0 ⩽ r < c i 0 \leqslant r < c_i 0⩽r<ci。
时间复杂度 O ( q n − r ) \mathcal{O}(q\sqrt{n - r}) O(qn−r)
- 大约是 1 0 4 × 1 0 6 = 1 0 7 10^4 \times \sqrt{10^6} = 10^7 104×106=107,完全可以过。
C++ Code for Solution1
#include <bits/stdc++.h>
using i64 = long long;
int calc(int x, int r) {
int ans = 0;
for (int i = 1; i * i <= x; i++) {
if (x % i == 0) {
ans += i > r;
if (i * i != x) {
ans += x / i > r;
}
}
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, q;
std::cin >> n >> q;
while (q--) {
int k, r;
std::cin >> k >> r;
int cnt = calc(n - r, r);
if (cnt >= k) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
return 0;
}
Solution2
考虑到 0 ⩽ r < n 0 \leqslant r < n 0⩽r<n,所以 n ≡ r ( m o d c i ) n \equiv r \pmod{c_i} n≡r(modci) 的 c i c_i ci 一定不会超过 n n n,否则就有 n ≡ n ( m o d N ) n \equiv n \pmod{N} n≡n(modN),其中 N > n N > n N>n。
因此我们可以直接从 1 1 1 到 n n n 枚举可能的模数 c i c_i ci,用一个 c n t cnt cnt 数组记录,例如 c n t [ i ] cnt[i] cnt[i] 表示有多少 c c c 满足 n ≡ i ( m o d c ) n \equiv i \pmod{c} n≡i(modc)。
最后判断的时候只要看是否有 c n t [ r ] ⩾ k cnt[r] \geqslant k cnt[r]⩾k 即可。
时间复杂度 O ( n + q ) \mathcal{O}(n + q) O(n+q)
C++ Code for Solution2
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, q;
std::cin >> n >> q;
std::vector<int> cnt(n);
for (int i = 1; i <= n; i++) {
cnt[n % i]++;
}
while (q--) {
int k, r;
std::cin >> k >> r;
if (cnt[r] >= k) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
return 0;
}
C. 老树也有几颗新芽
题目大意
M o r t i s Mortis Mortis 在穿过小巷时,遇到了一种奇特的硬币 G G G。一开始有 n n n 枚 G G G,分别记为 G i , i ∈ [ 1 , n ] G_i, \ i \in [1, \ n] Gi, i∈[1, n],对应的本地计数器分别为 c n t i , i ∈ [ 1 , n ] cnt_i, \ i \in [1, \ n] cnti, i∈[1, n]。一开始 ∀ c n t i = 0 \forall cnt_i = 0 ∀cnti=0。
现在你需要捡走地上所有的硬币。每一次操作你可以任选一枚硬币 G x G_x Gx 捡走,捡走后会按顺序发生以下事件:
① 随机选择一枚仍未被检走的 G y G_y Gy,使被选择的 G y G_y Gy 的本地计数器 c n t y : = c n t y + 1 cnt_y := cnt_y + 1 cnty:=cnty+1;
② 再在地上生成 c n t x cnt_x cntx 个新的硬币 G G G,均初始化 c n t = 0 cnt = 0 cnt=0 。
已知 M o r t i s Mortis Mortis 在每次操作时可以知道所有硬币的计数器 c n t i cnt_i cnti,因此总会采取最优策略。
求在最坏情况下,至少需要多少次操作才能将所有的硬币捡走。
Solution
最坏原则:在最坏情况下, c n t i : = c n t i + 1 cnt_i := cnt_i + 1 cnti:=cnti+1 操作会尽可能落到 c n t i cnt_i cnti 较小的 G i G_i Gi 上,我们也应优先捡起 c n t i cnt_i cnti 较小的 G i G_i Gi。因此对于 n n n 枚 ∀ c n t i = 0 \forall cnt_i = 0 ∀cnti=0 的硬币,若干次操作后总是会得到剩下一枚硬币 G G G 的 c n t = n − 1 cnt = n - 1 cnt=n−1,然后捡起这枚硬币,将问题转化为捡起 n − 1 n - 1 n−1 枚 ∀ c n t i = 0 \forall cnt_i = 0 ∀cnti=0 的硬币。
我们用 c d p [ i ] cdp[i] cdp[i] 表示将 i i i 枚 ∀ c n t j = 0 \forall cnt_j = 0 ∀cntj=0 的硬币全部捡起的最小操作次数, c d p [ 0 ] = 0 cdp[0] = 0 cdp[0]=0。
令 d p [ i ] = c d p [ i ] − c d p [ i − 1 ] dp[i] = cdp[i] - cdp[i - 1] dp[i]=cdp[i]−cdp[i−1],这样 d p [ i ] dp[i] dp[i] 的定义就是从 i i i 枚 ∀ c n t j = 0 , j ∈ [ 1 , i ] \forall cnt_j = 0, \ j \in [1, \ i] ∀cntj=0, j∈[1, i] 的硬币变为 i − 1 i - 1 i−1 枚 ∀ c n t j = 0 , j ∈ [ 1 , i − 1 ] \forall cnt_j = 0, \ j \in [1, \ i - 1] ∀cntj=0, j∈[1, i−1] 的硬币的最小操作次数。
下面求解 d p [ i ] dp[i] dp[i]。
我们可以根据最坏原则手玩一下前 8 8 8 项 d p dp dp 值,发现会得到 1 , 2 , 3 , 5 , 6 , 10 , 11 , 16 1, \ 2, \ 3, \ 5, \ 6, \ 10, \ 11, \ 16 1, 2, 3, 5, 6, 10, 11, 16。
例如 d p [ 5 ] = 6 dp[5] = 6 dp[5]=6 的模拟如下( [ k ] [k] [k] 表示计数器的值为 k k k):
[ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 1 ] [ 2 ] [ 0 ] [ 3 ] [ 4 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [0][0][0][0][0] \\ [0][0][0][1] \\ [0][1][1] \\ [1][2] \\ [0][3] \\ [4] \\ [0][0][0][0] [0][0][0][0][0][0][0][0][1][0][1][1][1][2][0][3][4][0][0][0][0]
再有 d p [ 7 ] = 11 dp[7] = 11 dp[7]=11 的模拟如下:
[ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 1 ] [ 1 ] [ 1 ] [ 2 ] [ 0 ] [ 2 ] [ 2 ] [ 2 ] [ 3 ] [ 0 ] [ 0 ] [ 4 ] [ 1 ] [ 4 ] [ 0 ] [ 5 ] [ 6 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [0][0][0][0][0][0][0] \\ [0][0][0][0][0][1] \\ [0][0][0][1][1] \\ [0][1][1][1] \\ [1][1][2] \\ [0][2][2] \\ [2][3] \\ [0][0][4] \\ [1][4] \\ [0][5] \\ [6] \\ [0][0][0][0][0][0] [0][0][0][0][0][0][0][0][0][0][0][0][1][0][0][0][1][1][0][1][1][1][1][1][2][0][2][2][2][3][0][0][4][1][4][0][5][6][0][0][0][0][0][0]
对于这个