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)\)

罚时解析:

正解:我们可以使用 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);
    }
}

总结

  1. 倒着抢首 A 好玩。
  2. map 常数大。
  3. STL 常数大。
  4. hash 是个好东西。
posted @ 2026-08-19 21:52  AKCoder  阅读(35)  评论(0)    收藏  举报