7.20模考总结

本周进行了标准CSP-S组复赛OI模拟赛
得分情况

题目 做法 预计得分 实际得分
A球袋统计 标记数组 100 100
B叁的倍数 数学、字符串 100 100
C小信找硬币 搜索、染色、集合 0 0
D最大复合值 背包 0 0
E曼哈顿多交椭圆 前缀和、二分、数学 10 30

A 球袋统计

局部截取_20250720_184117
局部截取_20250720_184154
局部截取_20250720_184213
局部截取_20250720_184226

没想到提高组的题目这么水,心情:grinning_face:

直接用标记数组、用cnt统计答案即可(其实这里有个小坑,就是map,因为访问map的时间复杂度是 O(log size) ),大数据会超时,我们还是换成标记数组好

code

#include <bits/stdc++.h>
using namespace std;
int q;
int res[1000010], cnt;
int main() {
	cin >> q;
	while (q--) {
		int op, x;
		cin >> op;
		if (op == 1) {
			cin >> x;
			if (res[x] == 0) cnt++;
			res[x]++;
		} else if (op == 2) {
			cin >> x;
			if (res[x] == 1) cnt--;
			res[x]--;
		} else if (op == 3) {
			cout << cnt << "\n";
		}
	}
	return 0;
}

B 叁的倍数

局部截取_20250720_184708
局部截取_20250720_184721

太水了,提高组太水了!:grin::grin::grin:

学过小学数学的都知道,3的倍数的特征是——所有数位加起来是3的倍数

我们就用字符串存数n,用res来统计数位和,判断res是不是3的倍数即可

code

#include <bits/stdc++.h>
using namespace std;
int n;
string s;
int main() {
	cin >> n;
	while (n--) {
		cin >> s;
		int res = 0;
		for (int i = 0 ; i < s.size() ; i++) {
			res += s[i] - '0';
		}
		if (res % 3 == 0) {
			cout << "Yes\n";
		} else {
			cout << "No\n";
		}
	}
	return 0;
}

C 小信找硬币

局部截取_20250720_185120
局部截取_20250720_185208
局部截取_20250720_185221

呃,稍微有点水,心情:sweat_smile:

赛中我脑抽把 n \times m \le10^5 看成了 n,m \le 10^5
所以我就一直想用1维dp做,但是一直做不出来,呜呜呜

正解:

是一个连通块染色,输入完遍历每一个没有被访问并且不是墙的点,随后编号+1,进行染色,在维护一个f数组来存储第编号为i的联通块的钱的总数量

随后遍历每一个墙,把墙的上下左右的染色点的编号装进set里面(因为set可以去重)(如果是墙就不用装),随后把 f[set里的染色编号] 的和打擂台就可以了

(注意,需要特判,万一里面没有墙就没了,所以一开始答案应该是f数组里的最大值)

code

const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, -1, 0, 1};
vector<vector<int>> a, vis;
vector<int> f;
void dfs(int x, int y, int sum, int& res) {
	vis[x][y] = sum;
	res += a[x][y];
	for (int i = 0; i < 4; i++) {
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx >= 0 && ny >= 0 && nx < n && ny < m && vis[nx][ny] == -1 && a[nx][ny] != -1) {
			dfs(nx, ny, sum, res);
		}
	}
}
	
	int sum = -1;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (a[i][j] != -1 && vis[i][j] == -1) {
				sum++;
				int res = 0;
				dfs(i, j, sum, res);
				f.push_back(res);
			}
		}
	}
	int ans = 0;
	if (!f.empty()) {
		ans = *max_element(f.begin(), f.end());
	}
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (a[i][j] == -1) {
				set<int> st;
				int cnt = 0;
				for (int k = 0; k < 4; k++) {
					int ni = i + dx[k];
					int nj = j + dy[k];
					if (ni >= 0 && nj >= 0 && ni < n && nj < m && vis[ni][nj] != -1) {
						int tep = vis[ni][nj];
						if (st.find(tep) == st.end()) {
							st.insert(tep);
							cnt += f[tep];
						}
					}
				}
				ans = max(ans, cnt);
			}
		}
	}

D 最大复合值

局部截取_20250720_190207
局部截取_20250720_190237
局部截取_20250720_190252
局部截取_20250720_190304

啊怎么能这么难!!!

:unamused_face::unamused_face::unamused_face::unamused_face::unamused_face:

题解:

当固定要在序列 p 中使用的 K 个下标时,序列 p 具有如下性质:

对任意两条线性函数

f_i(x)=A_i x + B_i> f_j(x)=A_j x + B_j

f_i\bigl(f_j(x)\bigr)\;>\;f_j\bigl(f_i(x)\bigr) \;\Longleftrightarrow\; \frac{A_i-1}{B_i}\;>\;\frac{A_j-1}{B_j},

并且这一大小关系与 x 无关,只取决于 \tfrac{A_i-1}{B_i}\tfrac{A_j-1}{B_j} 的大小。

因此,当你选定了 K 个下标要插入到 p 中时,为了让

f_{p_1}\bigl(f_{p_2}(\cdots f_{p_K}(1)\cdots)\bigr)

达到最大值,必须让

\frac{A_{p_1}-1}{B_{p_1}} \;\ge\; \frac{A_{p_2}-1}{B_{p_2}} \;\ge\; \cdots \;\ge\; \frac{A_{p_K}-1}{B_{p_K}}.

为简化描述,不妨预先将 (A,B) 对按

\frac{A_1-1}{B_1}\;\ge\;\frac{A_2-1}{B_2}\;\ge\;\cdots\;\ge\;\frac{A_N-1}{B_N}

排序。此时,原问题可等价地表述为:

在区间 \{1,2,\dots,N\} 中选出一个严格增序的长度为 K 的下标序列

1 \le p_1 < p_2 < \cdots < p_K \le N

使得

f_{p_1}\bigl(f_{p_2}(\cdots f_{p_K}(1)\cdots)\bigr)

最大。

这一问题可用下面的动态规划在 O(NK) 时间内求解:

  1. 定义状态

dp_{i,j} = \max_{\substack{p_{K-i},\dots,p_K \\\text{已选且} \,p_{K-i}\ge j}} f_{p_{K-i}}\bigl(f_{p_{K-i+1}}(\cdots f_{p_K}(1)\cdots)\bigr),

即:在已经决定了末尾 i 个下标,且第 K!-!i 个下标至少为 j 的情况下,所能达到的最大函数值。
2. 转移方程
考虑要不要在位置 j 上选一个下标:

dp_{i,j} = \max\Bigl( dp_{i,j+1}, \;A_j \times dp_{i-1,j+1} + B_j \Bigr).

  • 不选 j:保持前面已经选了 i 个下标,最小下标要从 j+1 开始,值为 dp_{i,j+1}
  • 选 j:那就用一次函数 f_j,得到 A_j\cdot(dp_{i-1,j+1})+B_j
  1. 边界条件

dp_{0,j} = 1 \quad(\text{相当于还没应用任何函数时,初始值为 }1).

  1. 答案

\max_{1\le p_1<\cdots<p_K\le N} f_{p_1}\bigl(f_{p_2}(\cdots f_{p_K}(1)\cdots)\bigr) = dp_{K,1}.

因为状态数是 O(NK),每次转移 O(1),所以总体时间复杂度为 O(NK)。

所以我们只需要先排序,再附初始值,最后状态转移即可

code

bool cmp(pair <ll, ll> x, pair <ll, ll> y) {
	return x.first * y.second + x.second < y.first * x.second + y.second;
}
	sort(p + 1, p + n + 1, cmp);
	for (int i = 0 ; i <= n ; i++) {
		dp[i][0] = 1;
	}
	for (int i = 1 ; i <= n ; i++) {
		for (int j = 1 ; j <= k ; j++) {
			dp[i][j] = max(dp[i - 1][j], p[i].first * dp[i - 1][j - 1] + p[i].second);
		}
	}

E 曼哈顿多交椭圆

局部截取_20250720_193118
局部截取_20250720_193227
局部截取_20250720_193241
局部截取_20250720_193256

02984BC0

太难了!!!心情:scream:

赛中我输出0居然骗了30分,哈哈哈哈哈哈哈哈

正解:
局部截取_20250720_193556
长截图_20250720_193616

1 个赞

這不是 S 組難度吧

1 个赞

這要是S組難度那我們班做的算什麽 :sweat_smile:

加油!

这也就普及组难度吧

最后一题可能能到提高难度

T5 我看过了也是普及组

我上普及的时候都做过这写题

不是做的比我高

这场挂分快挂没了

不好意思,但是我们提高基础也是这套题。

e你是不是 at 錯人了?

這是 @冯俊骁 的模考題目

但是我確定這是普及組難度

1 个赞

就是提高啊

1 个赞

可能是老师心地善良。人美心善。
@叶子宁 亲爱の老师是不是这样?

1 个赞

我覺得就是普及你難度吧?

1 个赞

o原來這是你們的老師呀

1 个赞

(帖子已被作者删除)

你又不是不会简体,用简体行不?不然每次都翻译很烦的啊

1 个赞