20260819 - contest
一、比赛基本情况
- 比赛日期:2026 年 8 月 19 日
- 比赛时长:3 小时
- 比赛得分:6 分
- 比赛排名:第 3 名
二、比赛整体回顾
这场比赛我按照 A, C, D, F', E, B, F 的顺序做题。
前面 \(4\) 题还算简单,后面两题一个马力题,一个卡常题。
免责声明:时间复杂度计算都是单组数据,而且只贴关键代码。
A - Olesya and Rodion
题意:输出一个严格大于零的 \(n\) 位数,且能被 \(t\) 整除。如果这样的数字不存在,则输出 \(-1\)。
tag:模拟,构造,数学
个人难度:800
实际难度:1000
思路:因为 \(t \le 10\),所以分 \(t = 10\) 和 \(t < 10\) 分类讨论:
- 若 \(t < 10\),则全部构造 \(t\),一定是 \(t\) 的倍数。
- 若 \(t = 10\),如果 \(n=1\),则不能构成 \(10\) 的倍数,否则直接构造 \(1000\dots0\)。
时间复杂度:\(O(n)\)。
空间复杂度:\(O(1)\)。
int main() {
scanf("%d%d", &n, &t);
if (t == 10) {
if (n == 1) return puts("-1"), 0;
printf("%d", 1);
for (int i = 2; i <= n; i++) printf("0");
} else {
for (int i = 1; i <= n; i++) printf("%d", t);
}
return 0;
}
B - Make Almost Equal With Mod
题意:你要选择一个 \(k\),使得 \(a_i \bmod k\) 的取值恰好两组。
tag:枚举,模拟,数学
个人难度:1400
实际难度:1200
思路:考虑取模操作在二进制里面的操作过程,\(\bmod 2^k\) 就是取二进制后 \(k\) 位;即操作步骤就是枚举 \(k\),判断是否可行;一定能枚举到一个分界点使得恰好能分成两组。
时间复杂度:\(O(nw)\),\(w\) 为 \(\log_2 10^{18}\),为了不在复杂度内计算常数。
bool check(ll k, ll l) {
for (int i = 2; i <= n; i++) if (a[i] % k != l) return true;
return false;
}
int main() {
int test;
scanf("%d", &test);
for (; test--; ) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
sort(a + 1, a + n + 1);
for (ll pw = 2; 114514; pw *= 2) {
if (check(pw, a[1] % pw)) {
printf("%lld\n", pw);
break;
}
}
}
return 0;
}
PS:因为没开 long long CE 了(少吃了一发罚时)
C - Not Adding
题意:求不同 \(\gcd(a_i,a_j)\) 的个数。
tag:模拟,数学
个人难度:1600
实际难度:1900
CF 乱评难度。
思路:一个典型的思路,\(\operatorname{lcm}\) 是对应 \(\gcd\) 的倍数(但这不重要);我们可以枚举每个数是否能作为最大公约数出现,这时候可以在它的所有存在的倍数中做一个最大公约数,看结果是否为它本身;注意一点,原数组是不能算的。
时间复杂度:\(O(n\log n)\),调和级数,具体来说并不对,粗略估计一下就行了。
空间复杂度:\(O(n)\)。
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
isfind[a[i]] = true;
g[a[i]] = a[i];
}
int ans = 0;
for (int i = 1; i < N; i++) {
if (isfind[i]) continue;
for (int j = i; j < N; j += i) {
if (isfind[j]) g[i] = __gcd(g[j], g[i]);
}
if (g[i] == i && !isfind[i]) ++ans;
}
printf("%d\n", ans);
return 0;
}
D - Endless Walk
题意:求哪些点可以走到环上。
tag:Tarjan
个人难度:1800
实际难度:不知道,估计 1900(一个缩点都能到 1800)
思路:缩点,建返图或者倒着拓扑序;DP,判断每个点能否跑到环上,可以说是最简单的 DP。
时间复杂度:\(O(n+m)\)。
空间复杂度:\(O(n+m)\)。
void dp() {
vector<int> d(n + 1);
for (int i = 1; i <= n; i++) {
for (auto v : adj[i]) {
if (bel[i] != bel[v]) {
graph[bel[v]].push_back(bel[i]);
++d[bel[i]];
}
}
}
for (int i = 1; i <= scc; i++) way[i] = (siz[i] > 1);
int front = 1, rear = 0;
for (int i = 1; i <= scc; i++) if (!d[i])
q[++rear] = i;
while (front <= rear) {
int u = q[front++];
for (auto v : graph[u]) {
if (--d[v] == 0) {
q[++rear] = v;
}
}
}
for (int i = 1; i <= scc; i++) {
for (auto v : graph[q[i]]) {
dp[v] |= dp[q[i]] | way[q[i]];
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (way[bel[i]] | dp[bel[i]]) {
++ans;
}
}
printf("%d\n", ans);
}
E - Spy Syndrome 2
题意:求加密字符串骚操作变成的原串。
tag:Hash,DP
个人难度:1900
实际难度:1900
思路:首先跑一边倒着的哈希,存入 map 类似的容器,枚举起点,长度,倒着判断是否可行。
时间复杂度:\(O(n\max |w|\log n)\)。
空间复杂度:\(O(n)\)。
罚时解析:
- Time limit exceeded on test 10,没有判断大于 \(1000\) 的情况。
- Time limit exceeded on test 10,以为是常数太大了,开了个 O2,没啥用。
- Time limit exceeded on test 10,数字打错了。
- Wrong answer on test 54 如果 hash 值为 0,就会误判;
- Wrong answer on test 54,以为是 hash 卡了,换了个 base。
- Wrong answer on test 54,改了个寂寞。
- Time limit exceeded on test 67,常数太大了。
- Accepted
正解:我们可以使用 O2,O3 优化,map 部分可以用平板电视的 hash_table 来实现,然后就过了,细节 while (1)。
F - Hotel G
题意:支持查询房间,插入房间,删除房间的功能,求第一个空位置大于 \(x\) 的位置。
tag:线段树
个人难度:1800
实际难度:不知道,预估 1800
思路:考虑线段树记录 \(maxv, maxl, maxr\) 分别表示最大的空隙,包含左端点的最大空隙,包含右端点的最大空隙,下传标记就是判断标记是哪一个,合并就类似小白逛公园;查询就是线段树二分
时间复杂度:\(O(n\log n)\)。
空间复杂度:\(O(n)\)。
struct Info {
ll maxv, maxl, maxr;
int siz;
};
struct Tag { ll upd; };
struct Node {
Info val;
Tag t;
int siz;
} seg[N * 4];
Info operator + (const Info &A, const Info &B) {
Info C;
C.siz = A.siz + B.siz;
if (A.maxv == A.siz) C.maxl = A.maxv + B.maxl;
else C.maxl = A.maxl;
if (B.maxv == B.siz) C.maxr = B.maxv + A.maxr;
else C.maxr = B.maxr;
C.maxv = max({A.maxv, B.maxv, A.maxr + B.maxl});
return C;
}
void update(int id) {
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void build(int id, int l, int r) {
seg[id].val = {r - l + 1, r - l + 1, r - l + 1, r - l + 1};
seg[id].siz = r - l + 1;
if (l == r) {
} else {
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
void settag(int id, Tag t) {
if (t.upd == 1) {
seg[id].t = t;
seg[id].val.maxl = seg[id].val.maxr = seg[id].val.maxv = 0;
} else {
seg[id].t = t;
seg[id].val.maxl = seg[id].val.maxr = seg[id].val.maxv = seg[id].siz;
}
}
void pushdown(int id) {
if (!seg[id].t.upd) return;
settag(id * 2, seg[id].t);
settag(id * 2 + 1, seg[id].t);
seg[id].t.upd = 0;
}
void modify(int id, int l, int r, int ql, int qr, Tag val) {
if (ql == l && qr == r) {
settag(id, val);
} else {
int mid = (l + r) / 2;
pushdown(id);
if (qr <= mid) modify(id * 2, l, mid, ql, qr, val);
else if (ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, val);
else {
modify(id * 2, l, mid, ql, mid, val);
modify(id * 2 + 1, mid + 1, r, mid + 1, qr, val);
}
update(id);
}
}
int search(int id, int l, int r, int d) {
if (l == r) {
return l;
} else {
int mid = (l + r) / 2;
pushdown(id);
if (seg[id * 2].val.maxv >= d) return search(id * 2, l, mid, d);
else if (seg[id * 2].val.maxr + seg[id * 2 + 1].val.maxl >= d) return mid - seg[id * 2].val.maxr + 1;
else return search(id * 2 + 1, mid + 1, r, d);
}
}
总结
- 倒着抢首 A 好玩。
- map 常数大。
- STL 常数大。
- hash 是个好东西。

浙公网安备 33010602011771号