NOTE
A - Social Experiment
关键词:签到
Code
// Problem: CF 2184 A// Contest: Codeforces - Codeforces Round 1072 (Div. 3)// URL: https://codeforces.com/contest/2184/problem/A// Time: 2026-05-14 22:55:01#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
void solve(){ int n; cin >> n;
if (n == 2) cout << 2 << endl; else if (n == 3) cout << 3 << endl; else cout << n % 2 << endl;}
int main(){ fastio();
int T = 1; cin >> T;
while (T--) solve();
return 0;}B - Hourglass
关键词:分类讨论
思路
题目问的是在第 分钟这个瞬间,沙漏的上半部分还剩多少沙子。
我们首先考虑:在翻转之前,沙子能不能全漏完? 这将问题分成了两大类:(能漏完/刚好漏完) 和 (漏不完)。
当 时,在翻转之前沙子就漏光了,因此每次反转都会重置沙漏成上半部分满,下半部分空的初始状态。既然每次反转都会重置,我们就不关心之前翻了多少次,只需要关心当前处于这轮周期的第几分钟,也就是 m % k。初始时上半部分有 的沙子,漏了 m % k 分钟,理论上还剩 s - m % k 的沙子。但如果 m % k 已经大于 了(说明沙子早就漏光了),剩下的沙子不能是负数,所以是 。
当 时,沙漏的状态会在偶数周期和奇数周期之间变化。对于偶数周期,当周期开始时,上半部分总是有 的沙子;对于奇数周期,当周期开始时,上半部分总是有 的沙子。我们首先需要计算经历了多少个完整的周期,即 cnt = m / k。在当前周期内度过的时间仍然是 m % k,讨论当前周期的奇偶性,然后拿总数 或者 减掉 m % k 即可。
NOTE刚开始考虑的时候,并没有把问题分成这两类,而是统一去考虑奇偶周期了。思维还是不够清晰。
Code
// Problem: CF 2184 b// Contest: Codeforces - Codeforces Round 1072 (Div. 3)// URL: https://codeforces.com/contest/2184/problem/b// Time: 2026-05-15 15:53:27#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
void solve(){ int s, k, m; cin >> s >> k >> m;
if (s == k) // 可以合并成 s <= k cout << s - m % k << endl; else if (s < k) cout << max(0, s - m % k) << endl; else { int cnt = m / k; if (cnt % 2) cout << k - m % k << endl; else cout << s - m % k << endl; }}
int main(){ fastio();
int T = 1; cin >> T;
while (T--) solve();
return 0;}C - Huge Pile
关键词:一维BFS
思路
我们发现一个性质:同一个深度的节点最多只能有两个不同的值,因此整个拆分过程中产生的状态其实很少。大约只有 个状态。并且我们需要知道到达每种状态的“最小步数”,考虑使用一维BFS求解。
注意:过程中发现 时剪枝。这道题的 map 类似于记忆化搜索,当我们已经搜过这个状态,就不需要再搜了。
Code
// Problem: CF 2184 C// Contest: Codeforces - Codeforces Round 1072 (Div. 3)// URL: https://codeforces.com/contest/2184/problem/C// Time: 2026-05-16 12:00:31#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
int n, k;
int bfs(){ queue<int> q; map<int, int> dist;
q.push(n), dist[n] = 0;
while (!q.empty()) { int t = q.front(); q.pop();
if (t == k) return dist[t];
if (t < k) continue;
int ls = t / 2, rs = (t + 1) / 2;
if (!dist.count(ls)) { dist[ls] = dist[t] + 1; q.push(ls); } if (!dist.count(rs)) { dist[rs] = dist[t] + 1; q.push(rs); } }
return -1;}
void solve(){ cin >> n >> k;
cout << bfs() << endl;}
int main(){ fastio();
int T = 1; cin >> T;
while (T--) solve();
return 0;}D - Unfair Game
关键词:组合数学,推公式
思路
考虑一个数变为 的最小步数,显然能除以 就一定要除以 ,否则只能减 。
对于任意的初始数字,考虑它的二进制形式。每移出一个二进制位(除以 ),需要 步,如果碰到了 ,需要先把它减成 再除以 ,因此 。
要求我们统计在 中,有多少个数字满足 ,我们将 范围内的数分为两部分:
-
数字 本身:
它的长度是 ,包含 1 个
1。所以 。如果 ,答案直接
+1。 -
数字 :
这些数字的最高位必定在第 位(从右往左数,从 1 开始),其中 。
- 对于一个固定的长度 ,它的最高位必定是
1。 - 剩下的 个低位,可以是
0也可以是1。假设其中有 个1()。 - 那么这个数字的总
1个数为 。 - 根据公式,。
- 对于一个固定的长度 ,它的最高位必定是
我们要找的是 的数字个数。
因为在一个长度为 的区域里选出 个位置填 1,方案数就是组合数 。
由于 比较小,使用杨辉三角递推出组合数即可。
Code
// Problem: CF 2184 D// Contest: Codeforces - Codeforces Round 1072 (Div. 3)// URL: https://codeforces.com/contest/2184/problem/D// Time: 2026-05-16 12:39:16#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 55;
int c[N][N];
void init(){ for (int i = 0; i <= 50; i++) { c[i][0] = c[i][i] = 1; for (int j = 1; j < i; j++) c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; }}
void solve(){ int n, k; cin >> n >> k;
int d = __lg(n);
ll res = 0; for (int i = 1; i < d; i++) for (int j = 0; j <= i; j++) if (i + j + 1 > k) res += c[i][j];
if (d + 1 > k) res++;
cout << res << endl;}
int main(){ fastio();
int T = 1; cin >> T;
init();
while (T--) solve();
return 0;}E - Exquisite Array
关键词:并查集,离线处理
思路
首先求一个差分数组,即 ,长度为
现在问题变成了:在数组 中,一个连续的子段如果所有元素都 ,那么它就对应原数组中的一个满足要求的数组。
假设这个连续子段的长度为 ,这个区间的内部会产生 个合法子段。
TIP对于某个特定的 ,我们需要找出 数组中所有完全由 的元素组成的“连续段”,算出每个连续段长度 对应的 ,将它们全部累加起来,就是答案。
假设我们从小到大枚举 并计算结果,随着 的增大,前面已经合法的 会变得不合法,我们考虑从大到小倒序枚举 。
随着 的减小,越来越多的 会变得合法(我们可以把它们看作是一个个被“激活”的节点)。原本断开的连续段,会因为新节点的“激活”而被连接(合并)在一起,使用并查集动态维护联通块。
NOTE因为我们要计算贡献,当我们激活一个位置 时,我们都需要计算包含 的这个连续合法区间现在的长度。
因此使用并查集维护连通性和连通块的长度。
完整流程
-
预处理:将所有 的位置,按照值域放进若干个桶里。使用
vector<int> pos[x],存下所有 的下标 。(注意unordered_map容易被卡,尽量使用vector。 -
初始化并查集和维护变量:维护一个变量
cur代表当前的合法子段总数。使用vector<bool> active(n, false)记录每个差值位置是否被“激活”。 -
倒序遍历并计算结果。当遍历到某一个 时:
-
拿出
pos[k]里的所有下标 。 -
将下标 标记为“已激活”。它本身形成了一个长度为 的连通块,此时
cur增加 。 -
检查左邻居 :如果 也是“已激活”的,我们就需要把 和 所在的连通块合并。
合并前,我们先从
cur中减去这两个独立连通块原本的贡献。合并后,把它们的大小相加得到新的 ,再给
cur加上新连通块的贡献 。 -
检查右邻居 : 同理,如果 也是“已激活”的,执行相同的合并与贡献更替操作。
-
把所有的 处理完之后,当前的
cur就是 对应的答案,存入结果数组。
-
Code
// Problem: CF 2184 E// Contest: Codeforces - Codeforces Round 1072 (Div. 3)// URL: https://codeforces.com/problemset/problem/2184/E// Time: 2026-05-13 22:20:17#include <bits/stdc++.h>using namespace std;
// clang-format off#define endl '\n'#define all(x) (x).begin(), (x).end()#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);// clang-format on
using ll = long long;using ull = unsigned long long;using pii = pair<int, int>;using pdd = pair<double, double>;using pll = pair<long long, long long>;using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};const int inf = 0x3f3f3f3f;const int N = 0;
struct DSU{ vector<int> parent; vector<int> sz; int comp_cnt;
DSU(int n) { parent.resize(n + 1); iota(parent.begin(), parent.end(), 0); sz.assign(n + 1, 1); comp_cnt = n; }
int find(int i) { if (parent[i] == i) return i; return parent[i] = find(parent[i]); }
bool unite(int i, int j) { int pi = find(i); int pj = find(j); if (pi != pj) { if (sz[pi] < sz[pj]) swap(pi, pj); parent[pj] = pi; sz[pi] += sz[pj]; comp_cnt--; return true; } return false; }
bool connected(int i, int j) { return find(i) == find(j); }
int size(int i) { return sz[find(i)]; }};
void solve(){ int n; cin >> n;
vector<int> a(n), d(n - 1); for (int i = 0; i < n; i++) cin >> a[i];
vector<vector<int>> mp(n); for (int i = 0; i < n - 1; i++) { d[i] = abs(a[i] - a[i + 1]); mp[d[i]].push_back(i); }
DSU dsu(n); ll cur = 0;
vector<ll> res(n); vector<bool> active(n, false);
auto calc = [](ll sz) { return sz * (sz + 1) / 2; };
for (int k = n - 1; k >= 0; k--) { for (auto i: mp[k]) { active[i] = true; cur += calc(1);
if (i > 0 && active[i - 1]) { ll sz1 = dsu.size(i - 1); ll sz2 = dsu.size(i);
cur -= calc(sz1) + calc(sz2); dsu.unite(i - 1, i); cur += calc(dsu.size(i)); }
if (i < n - 2 && active[i + 1]) { ll sz1 = dsu.size(i + 1); ll sz2 = dsu.size(i);
cur -= calc(sz1) + calc(sz2); dsu.unite(i + 1, i); cur += calc(dsu.size(i)); } } res[k] = cur; }
for (int k = 1; k < n; k++) cout << res[k] << " "; cout << endl;}
int main(){ fastio();
int T = 1; cin >> T;
while (T--) solve();
return 0;}