YACS-2022.6-银组
中途吃了个饭做了个核酸,压线赛时 AK 了,希望不要挂分。
T1 子集和
题目描述
子集和问题是指,给定 个数字 ,再给定一个目标 ,有多少种方法,能够选出一些数字,使得它们的和等于 。注意每个数字可以重复选择多次。
小爱希望计算一些带有限制的子集和问题,她想知道,对于 ,如果规定不能选择 ,那么还有多少种方法,可以选出一些数字,使得它们的和等于目标 ?答案对 取模。
。
大体思路
对于 ,都需要求出删去第 个数的方案数,因此考虑动态规划。
记 表示前 个数之和为 的方案数,。由于每个数可以重复选取,,其中外层循环 ,内层循环 。
同时,记 表示 开始之后的数之和为 的方案数,。其状态转移方程与 类似但 的方向相反。
容易想到,对于删去 ,相当于前 个数和 开始之后的数组成 的方案数,可以枚举前 个数的和,则答案为 。
这样的做法时空复杂度均为 ,时间上能够通过,但即使开 int
也可能 MLE,因此将 利用滚动数组优化,并且算出一组 后立刻计算相应的 ,这样空间常数得到优化,用 int
可以通过。
部分代码
f[0][0] = 1; rep(i, 1, n) { rep(j, 0, m) { f[i][j] = f[i - 1][j]; if(j >= a[i]) f[i][j] = (f[i][j] + f[i][j - a[i]]) % mod; } } g[(n + 1) & 1][0] = 1; Rep(i, n, 1) { rep(j, 0, m) { g[i & 1][j] = g[(i + 1) & 1][j]; if(j >= a[i]) g[i & 1][j] = (g[i & 1][j] + g[i & 1][j - a[i]]) % mod; ans[i] = (ans[i] + 1ll * f[i - 1][j] * g[(i + 1) & 1][m - j] % mod) % mod; } } rep(i, 1, n) writeln(ans[i]);
T2 路径问题
题目大意
给定一张图,有 个点,每个点有且只有一个后继,第 个点的后继编号为 ,这条边的权重为 。
给定一个整数 ,从每个点出发,沿着给定的后继遍历,经过 条边后停止,请分别求出这些路径上的最大权重。
。
大体思路
如果你想到了许多复杂度做法,你可能已经用到了 LCA,那你为什么没有想到倍增呢?
倍增简单题,如果整个图是链状,那么显然是 ST 表的模板。现在有了后继,只需要一个辅助数组。
因此,我们定义 表示节点 出发经过 条边得到的最大权值, 表示 的 级后继。
那么显然有 ;
f[i][p] = max(f[i][p - 1], f[g[i][p - 1]][p - 1]); g[i][p] = g[g[i][p - 1]][p - 1];
对于 ,枚举二进制位,如果 的这一位为 ,那么 产生贡献,并且令 指向 级后继。
read(n); read(k); rep(i, 1, n) read(g[i][0]); rep(i, 1, n) read(f[i][0]); int P = log(k) / log(2) + 1; rep(p, 1, P) { rep(i, 1, n) { f[i][p] = max(f[i][p - 1], f[g[i][p - 1]][p - 1]); g[i][p] = g[g[i][p - 1]][p - 1]; } } rep(i, 1, n) { int ans = 0, u = i; rep(p, 0, P) if((k >> p) & 1) ans = max(ans, f[u][p]), u = g[u][p]; writeln(ans); }
T3 航海探险
就是这道题卡了我一会儿,然后我去做核酸了。
题目大意
在大海中,有 座不确定坐标的岛屿。接下来,会陆续发现这些岛屿之间的相对位置关系,在发现的过程中,系统也会询问一些岛屿之间的距离:
-
若有两岛屿的相对位置关系被发现了,则输入格式为
D a b e
,表示岛屿 在岛屿 的 方向 公里。 必须是ESWN
中的一个字母,分别表示东南西北四个方向,输入数据保证不会有矛盾的情况发生,但有些信息可能是重复多余的。 -
如果输入格式为
? a b
,表示需要查询岛屿 在岛屿 之间的距离。如果 与 之间的相对关系尚不确定,则输出?
。
注意,在计算距离时,所使用的定义是横纵坐标的差的绝对值之和,这种距离定义被称为城市距离或曼哈顿距离,而非常用的欧几里得距离。
大体思路
其实这道题如果是欧几里得距离也能做。
之间的位置关系有传递性和方向性,因此考虑用带权并查集维护向量。
用 记录节点 指向其父亲节点 的向量 。在路径压缩时由于路径压缩改变父亲节点,设当前节点为 ,父亲节点为 ,根节点为 ,则 ,而 在上一级函数中已经计算完成,因此直接令当前的 加上 即可。
合并时,对于已经在同一个集合内的节点不予操作。否则,假设两个节点为 ,其所在集合的根节点分别为 。根据 D a b e
语句,我们可以得到向量 ,现在要更新 。显然有 。
曼哈顿距离就是两个向量的 。
另外,,需要开 long long
。
int n, m, fa[maxn]; struct Vector { int x, y; } v[maxn]; inline int find(int u) { if(u == fa[u]) return u; int rt = find(fa[u]); v[u].x += v[fa[u]].x, v[u].y += v[fa[u]].y; return fa[u] = rt; } inline void merge(int x, int y, int dir, int dis) { // Dir x y dis, 0~3 = ESWN int f1 = find(x), f2 = find(y); if(f1 == f2) return; fa[f1] = f2; int dx = v[y].x - v[x].x, dy = v[y].y - v[x].y; if(dir == 0) v[f1] = {dx - dis, dy}; else if(dir == 1) v[f1] = {dx, dis + dy}; else if(dir == 2) v[f1] = {dis + dx, dy}; else v[f1] = {dx, dy - dis}; } signed main () { read(n); read(m); rep(i, 1, n) fa[i] = i; while(m --) { char op; int a, b, d; ReadChar(op); if(op == '?') { read(a); read(b); int f1 = find(a), f2 = find(b); if(f1 != f2) puts("?"); else writeln(abs(v[a].x - v[b].x) + abs(v[a].y - v[b].y)); } else { read(a); read(b); read(d); if(op == 'E') merge(a, b, 0, d); else if(op == 'S') merge(a, b, 1, d); else if(op == 'W') merge(a, b, 2, d); else merge(a, b, 3, d); } // rep(i, 1, n) printf("%lld: (%lld, %lld) ", i, v[i].x, v[i].y); // putchar('\n'); } return 0; }
T4 没有考试的天数
题目大意
给定 个数 ,求 之中有多少个数不是任何一个 的正整数倍。
大体思路
容斥模板题。用一个 位的二进制数表示是否选 。
每次对于所有选择的数求一个 ,然后根据数的个数的奇偶性判断答案加上或者减去 。时间复杂度 ,看上去似乎时间很紧,但其实其中的 是跑不满的。更精确的计算可以枚举 的个数然后用组合数导,但没啥意义。
需要注意的是, 个 的最大公约数可能超过 long long
的范围,因此当求得的 时直接 break;
。
代码非常简单,不给惹。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现