期末总结
序列 dp
woc 之前写的什么玩意
LIS
首先是正常的 dp。
memset(f, 1, sizeof f);
for(int i = 2; i <= n; i++)
for(int j = 1; j < i; j++)
if(a[j] < a[i])
f[i] = std::max(f[i], f[j] + 1);
然后是二分优化。
\(d_i\) 表示长度为 \(i\) 的 LIS 末尾元素最小值。
- 若 \(a_i>d_{size}\),\(size:=size+1\)。
- 否则替换掉 \(d\) 中第一个大于 \(a_i\) 的元素。
时间复杂度 \(O(n \log n)\)。
d[1] = a[1];
for(int i = 2; i <= n; i++)
if(a[i] > d[sz]) d[++sz] = a[i];
else *std::upper_bound(d + 1, d + sz + 1, a[i]) = a[i];
然后是树状数组优化。
发现其实是一个 \(i < j, a_i<a_j\) 的二位偏序问题。
树状数组 \(f\) 维护前缀最大值。
for(int i = 1; i <= n; i++) {
f[i] = query(a[i] - 1) + 1;
modify(a[i], f[i]);
}
同理,还可以用可爱的线段树。
LDS 同理。
LCS 不好玩。不写了。
P1106 低价购买
求最长下降子序列的长度及方案数。
先求出最长下降子序列的长度 \(f\)。
则
同理,可以使用树状数组优化。
在维护前缀最大时维护上述方案数。
可以将时间复杂度从 \(O(n^2)\) 降到 \(O(n\log n)\)。
序列 dp 里有许多规律。一个 dp 式的策略往往可以从另一个 dp 式的策略得到。
将两个式子一起转移即可。
背包 dp
感觉模板不难。直接上 Tricks。
下文中 \(v\) 表示体积,\(w\) 表示价值。
单调队列优化多重背包
md 之前写的什么煞笔玩意 重构
多重背包朴素转移为
观察发现转移中的的第二维 \(j-w_ik\equiv j\pmod{w_i}\),即 \(f_{i,j+w_ik}\) 能被 \(f_{i,j}\) 转移。
故对于每个物品,可以对其代价按照 \(\pmod{w_i}\) 分组。
令 \(v=v_i,c=\lfloor\frac{j}{v}\rfloor,d=j\bmod v\)。
故 \(j=vc+d\)。
将 \(v,c,d\) 带入朴素方程,得:
不难发现 \(c-k\) 就是在限制内还没有选的物品数量,令其为 \(k'\)。
故有
由于 \(k\) 的取值范围一定,\(c\) 为定值,所以 \(k'\) 的取值范围也一定。
可以考虑使用单调队列优化。
区间 dp
区间 dp,顾名思义,其本质是在大区间内枚举断点,将其分成两个小区间后选择最优方案进行转移。
特点为从小区间转移至大区间,自底向上求值。
通项公式为 \(f_{l,r}=\max\limits_{k=l}^{r-1}f_{l,k}+f_{k+1,r}+w_{l,r}\)。
主要 dp 板块需要借助题目感性理解一下。不然会很抽象。
下方由主观难度由易到难排序。
T1. [CQOI2007] 涂色
设状态 \(f_{l,r}\) 为 \([l,r]\) 内的最少涂色次数。
初始化 \(f_{i,i}=1\), \(f_{i,i+1}=\begin{cases}2&s_i\neq s_{i+1}\\1&s_1=s_{i+1}\end{cases}\) 。
显然,若两次涂色有重合部分,必然不是最优解。
枚举断点 \(k\),有转移方程 \(f_{l,r}=\min\limits_{l+1}^{r-1}f_{l,k}+f_{k+1,r}\)。
特判一下 \(s_l=s_r\) 的情况。 此时可以发现 \(f_{l,r}\),\(f_{l+1,r}\),\(f_{l,r-1}\) 等价。即 \(f_{l,r}=f_{l+1,r}=f_{l,r-1}\)。
故
T2. 表达式的亿种可能性
令 \(A\) 为 \(f_{l,k}\) 所有运算顺序结果的集合,\(B\) 为 \(f_{k+1,r}\) 所有运算顺序结果的集合。令 \(|A|=p,|B|=q\)。
故
即
由于 \(f_{l,r}=\{A_1,A_2,\cdots A_p\}op_k\{B_1,B_2,\cdots B_q\}\),即只要保证 \(A,B\) 的内部顺序不变,可以将 \(A,B\) 任意穿插,共有 \(C^p_{p+q}\) 种顺序。
故要给 \(f_{l,r}\) 乘上系数 \(C^p_{p+q}\)。
时间复杂度 \(O(n^3)\)。
T3. [JXOI2018] 守卫
这题主要是状态转移的细节不太好理解。
但是是第一道单切紫题,好诶 所以就把题解粘上来了
设状态 \(f_{l,r}\) 为在区间 \([l,r]\) 内要放的最少保镖数量。
看到题面第一眼的感觉是不会判两点能否连接。
第二眼发现可以用斜率判。
令 \(k_{l,r}\) 为横坐标为 \(l,r\) 的两点连线斜率。
有 \(k_{l,r}=\frac{h_r-h_l}{r-l}\)。
手搓几组样例,得 \(\forall x\in[l,r)\),有 \(k_{l,r}<k_{x,r}\) 。即对于 \(x\in[l,r)\),\((x,r)\) 的连线斜率最小。
且若 \(a\) 对于 \(b\) 可见,并满足 \(h_c>h_b\) 与 \(c>a\),则 \(a\) 对于 \(c\) 可见。
令 \(p=\{p_1,p_2\cdots p_m\}\) 为 \(r\) 点当前能够覆盖的点集。注:下文中 \(p\) 按照从右至左斜率单调递减的顺序排序。
注:能被覆盖的点可能不连续。但能被覆盖的点的斜率可以保证从右至左单调递减。
若想为点集连续,可能会导致 “\(p\) 为 \(r\) 点当前能够覆盖的最远点” 的错误贪心思路。
喜提20pt。
如图,\(r=G\) 时,\(p=\{E,D,B\}\)。
接下来是这题个人认为比较难处理的地方。
由于 \(p\) 不一定连续,故 \([p_{i+1}+1,p_i-1]\) 对于 \(r\) 不可见。
由上述性质得,对于 \(j\in[p_i,r)\),\(j=p_i\) 是唯一可以覆盖 \([p_{i+1}+1,p_i-1]\) 中至少一个点的点。 即在区间 \((p_{i+1},p_i)\) 右侧唯一能够覆盖到此区间的点为 \(p_i\)。(有点绕,建议自己手玩一下。)
如图,以 \(A\) 为右端点,\([D,A]\) 中能覆盖 \([G,E]\) 的唯一点为 \(D\)。
当然,也可以选择在 \(p_i-1\) 处放置保镖,从内部将点覆盖。
如在点 \(E\) 放置保镖。
故 \(p_i,p_i-1\) 至少有一处要放置保镖。
这样就成功的将区间 \([p_{i+1}+1,p_i]\) 或 \([p_{i+1}+1,p_i-1]\) 转换为了一个子问题。
区间 \([p_{k+1}+1,p_k-1]\) 的代价即为
\[f_{p_{i+1}+1,p_i-1}=\min\{f_{p_{i+1}+1,p_i-1},f_{p_{i+1}+1,p_i}\} \]
此时有转移方程:
时间复杂度 \(O(n^3)\),能拿 \(70\) pts。
考虑优化。
-
因为 \(p_i\) 是在 \([p_i,r)\) 中与 \(r\) 点连线斜率最小的,所以对于 \(x\in[l,p_i)\),若存在 \(f_{x,r}<f_{p_i,r}\),则 \(p_{i+1}=x\)。 可以考虑将 \(p_i\) 在循环里滚动处理。
-
可以注意到根据上述转移,当处理至 \(p\) 点时,区间 \([p,r]\) 可以保证被完全覆盖。有转移方程
\[f_{l,r}=\min\{f_{l,p-1,f_{l,p}}\}+f_{p+1,r} \]
干掉一个线性时间复杂度。时间复杂度 \(O(n^2)\)。
T4. 有味道的数字
先写一个暴搜。发现 \(n\leq 5000\) 的时候,\(|P|\leq 11\)。
即 \(n\leq 5000\) 的所有数字可以用 \(11451419191\) 表示。
这题主要是状态设计难想。
设 \(f_{l,r}\) 为区间 \([l,r]\) 能表示的所有数的集合。
令 \(A=\{1,1,4,5,1,4,1,9,1,9,1\}\)。
初始化 \(f_{l,r}\) 为 \(A_l\) ~ \(A_r\) 直接拼接的数字。如 \(f_{3,6}=\{4514\}\)。
显然有状态转移:
统计答案时,\(ans_x=\min\limits_{x\in f_{1,i}}i\)。
T5. [HAOI2016] 字符合并
然鹅并没有过。但是可以口胡一下。
看到二进制,一眼状压。
令 \(f_{l,r,s}\) 为区间 \([l,r]\) 合并为 \(s\) 获得的最大累计分数。
毕竟你得满足无后效性对吧。当区间内有多种情况或多条限制时,可以考虑加维转移。
那么对于每个状态 \(s\),可以枚举断点 \(i\) 使得
显然复杂度 \(O(2^{k-1}n^3)\) 会爆炸。
显然对于区间 \([l,r]\),最终合并长度为
所以只需要枚举 $k-1\ |i\ $ 的情况。
从合并后的串来转移。有转移方程
当 \(L=1\) 时,可以直接合并。
时间复杂度 \(O(\frac n k\times2^{k-1}n^2)\)。
个人认为区间 dp 是 dp 专题里面最简单的了。
主打一个感性理解和经验积累。真没啥套路。
想得出来就好写,想不出来就开摆。
最短路
应 Zikme 要求,不放代码。好像更抽象了
茅坑顿开。
0. 前言
下文中:
- \(u \to v\) 表示从 \(u\) 到 \(v\) 的单向边,\(w_{u,v}\) 为 \(u \to v\) 的边权。
- \(P(u,v)\) 表示 \(u\) 到 \(v\) 的最短路径。
- \(s\) 为源点,\(t\) 为终点。
- \(d_u\) 为 \(s\) 到 \(u\) 的最短路长度。(算法执行中)
- \(D_u\) 为 \(s\) 到 \(u\) 的最终最短路长度。
越简单的算法,证明越难想。——Zikme
1. dijkstra
1. 流程简述
算法核心是将图分为两个点集 \(S,\ T\)。其中 \(S\) 为已确定最短路点集,\(T\) 为未确定最短路点集。
算法流程:
重复以下操作,直至 \(T=\emptyset\):
-
从 \(T\) 中选取最短路长度最小的点 \(v\)。令 \(u\) 为 \(S\) 中存在 \(u \to v\) 的点中最短路长度最小的点。
-
进行松弛操作:\(d_v=\min\{d_v,d_u+w_{u,v}\}\)。
-
将 \(v\) 从 \(T\) 移至 \(S\),\(D_v=d_v\)。
2. 算法证明
显然,最短路有一个性质:对于一个最短路径,其前缀仍然是最短路。
故一定存在上述 \(u\) 点。
所以,只要证明加入 \(T\) 集合时 \(v\) 点的最短路已被确定(即 \(v\) 加入 \(S\) 时 \(D_v=d_v\)),dijkstra 算法得证。
考虑用反证法证明。
注:该证明只适用于边权非负的情况。这便是为什么 dijkstra 不能跑负权图。
设 \(u\) 为移动至 \(S\) 集合时 \(D_u<d_u\) 的点。
故当前最短路径与实际最短路径至少存在一个点不相同。
令 \(v_1,v_2\) 为当前最短路径与实际最短路径第一个不相同的点。
故 \(s\) 到 \(u\) 的当前最短路径为 \(p_1:s \to \cdots \to v_1 \to \cdots \to u\),\(s\) 到 \(u\) 的实际最短路径为 \(p_2:s \to \cdots \to v_2 \to \cdots \to u\)。
可将其简化为 \(p_1:s \to v_1 \to u,\ p_2:s\to v_2\to u\)。
显然,当 \(v_2\) 还未被更新到的时候,\(p_1\) 才能成为最短路径。
所以有 \(v_2\in T\)。
又因为 \(v_2\in p_2\) 且在 \(u\) 前,所以一定有 \(d_{v_2}<d_u\)。(注:只有在边权非负的情况下才成立。)
与 “\(d_u\) 为所有 \(p\in T\) 中 \(d_p\) 最小” 矛盾。故不存在 \(v_2\) 使得 \(p_1\) 不为最短路径。
dijkstra 算法得证。
3. 时间复杂度
暴力时间复杂度:
- 查找 \(T\) 集合中 \(d_u\) 的最小值,时间复杂度 \(O(n)\)。
- 松弛一条边,时间复杂度 \(O(1)\)。
对于图中 \(n\) 个点进行上述操作,总时间复杂度 \(O(n\times(n+1))=O(n^2)\)。
优化:
由于要查找 \(T\) 集合中 \(d_u\) 的最小值,可以使用支持修改的最值数据结构。
可选的有:
-
堆:每松弛一条边 \(u\to v\), 将 \(v\) 插入堆或修改 \(v\) 的权值,共 \(m\) 次;同时需取 \(n\) 次堆顶。时间复杂度 \(O((n+m)\log n)=O(m\log n)\)。
-
优先队列:由于优先队列不支持直接修改,故队列中有 \(m\) 个元素。总时间复杂度 \(O(m\log m)\)。
但是当数据为稠密图 (\(m\approx n^2\)) 时,上述优化时间复杂度均退化为 \(O(n^2\log n)\) 级别。还不如暴力
2. Bellman-Ford / SPFA 证明
1. 流程简述
其本质为松弛操作:\(d_u=\min\{d_u,d_v+w_{u,v}\}\)。
对图上的每一条边尝试松弛,共松弛 \(m\) 次。
意义显然。
其核心为:对于 \(P(u,v)\), 经过 \(n\) 次松弛即可保证正确性。
2. 算法证明
证明如下:
一次松弛至少可以将最短路的长度加 \(1\)。
由于最短路径不可能重复经过一条边(无负权环),最坏情况(最短路为链状)需要将 \(n\) 个点全部遍历。故 \(|P(u,v)|<n\)。
所以至多经过 \(n\) 次松弛操作即可保证正确性。得证。
综上所述,总时间复杂度为 \(O(n\times m)\)。
3. 算法优化
显然,大多数的松弛操作都是无效的。
而只有上一次被松弛过的节点(可能在最短路径中)所连接的边才可能会有有效的松弛操作。
考虑用队列维护。反正最坏情况下复杂度也为 \(O(n\times m)\)。
复杂度证明:
看不懂。不写了。反正非负权用 nm (指时间复杂度 \(O(n\times m)\)) 的 spfa。
至于怎么让这玩意死透,@changwenxuan 已经进行了详尽的研究。
干点正事。
3. Floyd 证明
1. 流程简述
设 \(g_{u,v}\) 为 \(u\to v\) 的最短路。
有转移方程 \(g_{u,v}=\min\{g_{u,v},g_{u,k}+g_{k,v}\}\)。
2. 算法证明
其原始方程为 \(g_{k,u,v}=\min\{g_{k-1,u,v},g_{k-1,u,k}+g_{k-1,k,v}\}\),表示只经过前 \(k\) 个点,\(u\to v\) 的最短路。
时间复杂度 \(O(n^3)\),空间复杂度 \(O(n^2)\)。
试证明第一维可以直接省略。
显然有 \(f_{k-1,u,v}=f_{k,u,v}\)。
如果没有,图上就有负环。最短路什么的也无所谓了。故可以将第一维直接省略。
4. Common Tricks
1. 最短路树
由于最短路的前缀也一定是最短路,所以对于单源最短路,其构成一棵有根叶向树。
主要用处是统计方案数和 dp。
2. 虚点
对于某些具有相同性质的点,可以另建一个点并将其与具有相同性质的点连边。
相当于将具有相同性质的点缩为一个点集,大幅减少边数及点数。
3. 分层图
运用 dp 思想,将不同决策导致状态不同的点装进不同的图内。
虽然一般来说这样的决策只能有一个。不然边数爆炸。
在大多数情况下等价于直接在图上进行多维 dp。
虽然我觉得建图要好看一些。
例题大赏
题目不按任何顺序排序。
[SDOI2010] 大陆争霸
第一眼看上去好像是最短路加了个强制拓扑。
也就是说当结界还没被破坏的时候,已经到达的机器人只能干等着。
在 dijkstra 中,机器人所在的点可以更新最短路。但拓扑图上该点的入度不为 \(0\),即结界产生器没有被全部破坏时,不能入队。
当炸掉一个结界产生器的时候,可以将其在拓扑图上连接的所有点的入度减一。
而当拓扑图上的一个点入度为 \(0\),即结界产生器全部被破坏的时候,便可以入队。
不用考虑当前是否有机器人在该点等待。即使没有,由于题目保证有解,所以若该点在 \(1\to n\) 的最短路上一定可达。之后也会有更优决策来替代它。
爆改 dijkstra。需要对算法流程有一定的理解。
CF1648E
在一带权无向图上,每次只能连续经过两条边,且代价为两条边边权之和的平方。求单源最短路。
数据范围 \(1\leq n\leq10^5,\ 1\leq m \leq 2\times10^5,\ 1\leq w\leq50\)。
暴力建图的话总边数为 \(m^2\),会爆炸。
考虑一个路径 \(u\to x\to v\),\(w_1=w_{u,x},\ w_2=w_{x,v}\)。
由于一次移动至少要经过两条边,所以上述代价和等价于将边权全部移到 \(x\to v\) 上。
这样的话 \(u\to x\) 就没那么重要了。在 \(w_2\) 一定的情况下共有 \(w=50\) 种可能的权值。
可以考虑以 \(w_1\) 的值构造分层图。
令 \((w,u)\) 为入边边权为 \(w\) 且入点为 \(u\) 的点集。但是由于其并不影响最终结果,可以将其作为一个点。
则对于点对 \((u,x,v)\),可以将 \(u\) 向 \((w_1,u)\) 连一条边权为 \(0\) 的边;将 \((w_1,u)\) 向 \(v\) 连一条边权为 \((w_1+w_2)^2\) 的边。
显然 \(u\to v\) 的代价等价于原图。
显然,对于一个点 \(u\)(假定其为路径的起始点),分层后其最大出度为 \(w=50\)。
总边数从 \(m^2\) 骤降到了 \(mw\)。
总时间复杂度为 \(O(m\log m)\) 左右。
分层图的各种妙妙玩法。
没思路时可以考虑转移边权或点权。
例如缩点,点权转边权等。
[HAOI2010] 道路
不管怎么说,双倍经验。
题意很简洁了。
对于每个源点 \(s\),先跑一遍 dijkstra。显然,若满足 \(dis_v=dis_u+w_{u,v}\),则 \(e(u,v)\) 一定在最短路上。
显然在 \(w_{u,v}>0\) 时,不存在 \(u,v\) 使得 \(dis_u=dis_v+w_{u,v} \wedge dis_v=dis_u+w_{u,v}\)。
因此,若将最短路径上的点从原图中取出,加入集合 \(V\),其构成的图一定为 DAG \(G(V,E)\)。
可以考虑将其进行拓扑排序。
其实是可以在 dijkstra 中排序的。dijkstra 的遍历顺序就是拓扑序。但是如果你硬要再写一遍也没人拦你
然后捏?
图上 dp。
设 \(G(V,E)\) 的源点(集)为 \(s'\),汇点(集)为 \(t'\)。
对于任意 \(E_i=e(u,v)\),设 \(out_i\) 为 \(s'\to u\) 的方案数,\(in_i\) 为 \(v\to t'\) 的方案数。
根据乘法原理,得 \(ans_i=in_i\times out_i\)。
\(out_i\) 好求。转移方程为 \(out_u=out_u+out_v[dis_u+w_{u,v}=dis_v]\ \ e(u,v)\in E\)。
事实上 \(in_i\) 和 \(out_i\) 的求法是一样的。
将 \(v\to t'\) 这部分建个反图 \(G'(V',E')\) 就可以达到同样的效果。
转移方程为 \(in_u=in_u+in_v[dis_v+w_{u,v}=dis_u]\ \ e(u,v)\in E'\)。
在 DAG 上算方案数有两种基本策略:
- 按照拓扑序 dp。
- 对于每个点 / 边分类讨论后,运用组合数学求方案数。
[NOIP2017] 逛公园
看到 \(k=50\),考虑按路径长度为一维度 dp。
设 \(f_{u,k}\) 为 从 \(1\) 到 \(u\) 路径长度为 \(d_u+k\) 的总方案数。
需要以倒拓扑序的顺序进行转移。
对于图上的每条边 \(e(u,v)\),有转移方程:
\(f_{u,k}=\sum\limits_{e(u,v)}f_{v,k-(d_u + w - d_v)} \ (d_u+w-d_v<k)\)。
其中 \(d_u+w_{u,v}-d_v\) 为较 \(d_v\) 经过 \(e(u,v)\) 多走的路程。
考虑按路径长度进行记忆化搜索求解。
具体实现可以建反图后搜索。
注:在搜索过程中如果遇见 \(0\) 环,则存在无穷种路径,输出 \(-1\)。可以通过维护每个点在当前路径长度被搜索到的次数判环。
若题目对某一条件存在限制,可以考虑将条件列入 dp 方程中求解。
[GZOI / GXOI2019] 旅行者
如果已知分组方案,对两组分别建立虚拟原点后跑最短路即可。
主要问题是如何分组。
随机化算法 shuffle 战神
设答案源点为 \(u\),汇点为 \(v\)。
将 \(K\) 分为大小相同的两组 \(K_1,K_2\),\(u\in K_1\wedge v\in K_2\) 的概率为 \(\frac 1 4\)。
故尝试 \(T\) 次全错的概率为 \(1-(\frac 3 4)^T\)。
尝试 \(20\) 次左右就行了。正确率为 \(99.68\%\)。
upd: NKOJ过不了。
二进制分组
按照 \(K_i\) 二进制上的每一位分组。
正确性证明:
显然源点与汇点是不同的点。故其至少有一个二进制位不同。将其按每一位的二进制分组,至少有一次源汇点会被分在不同组。
upd: NKOJ也过不了。
upd2: 没判 -1 导致的。
时间复杂度 \(O(m\log^2n)\)。
果国的奇妙旅行
下文中令 \(d_u\) 为点 \(u\) 的度数。
如题,求最优策略下的期望购票数。
期望:若有连续随机变量 \(x\),其期望 \(\mu_x=\sum\limits_{x\in D}x\times p_x\)。
其中 \(D\) 为定义域,\(p_x\) 为 \(x\) 出现的概率。
由于结果确定,通常期望 dp 采用倒推的方式。
设 \(f_u\) 为 \(u\) 到 \(n\) 的期望购票数。
将决策分开考虑。对于每个在点 \(u\) 抽到了去点 \(v\) 的票,有两种决策:移至 \(v\) 点或留在原地。
这样的状态设计可以让实际决策形成一个 DAG。
显然,若有 \(f_v\geq f_u\),最优决策为留在原地,期望为 \(\frac {f_u}{d_u}+1\)。
反之则可以移至点 \(v\),期望为 \(\frac {f_v} {d_u}+1\)。
故有转移方程
写起来挺好看的。但是在 \(f_u\) 的递推式中出现了自己。那就不好玩了。
不能用正常顺序求解。
注意到一个性质:\(\forall e(u,v):\min f_u\) 一定是由 \(\min f_v\) 转移过来的。
感觉和 dijkstra 有点像。考虑在 dijkstra 的过程中维护 \(f_u\)。
可以考虑把 \(f_u\) 替换成其他的可维护变量。
然后发现好像 \(f_u>f_v\) 的数量是可以维护的。顺便把 \(\sum\limits_{f_u>f_v}f_v\) 的值维护了。
每次选出当前已知的最小 \(f_v\)。对于每条边 \(e(u,v)\), 令 \(l_u\) 为满足 \(f_u>f_v\) 的数量,\(s_u\) 为满足 \(f_u>f_v\) 的 \(f_v\) 总和。
将上述方程化简得:
解得:
注:可以将 \(f_u\) 初始化为 \(\infin\)。~~如果后面有解的话,这反正也不是最优解。和 \(dis_u=\infin\) 一个意思。
优雅。这样就将 \(f_u\) 这个无法维护的量转为了 \(d_u\) 与 \(l_u\),\(s_u\) 三个可以维护的量。
图上期望 dp 的思路为:
- 确定最优策略
- 倒序 dp
- 若方程存在自环,可以将方程拆开后根据可维护变量进行转移。
[NKOJ8736] 指针分析
我也不知道这玩意为什么要放到最短路专题里。
将字母压位后 模 拟 即 可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】