2024 年 8 月题集
CF1887C Minimum Array
题目链接:https://codeforces.com/problemset/problem/1887/C
题意:
给定一个长度为 \(n\) 的整数序列,共 \(q\) 此操作,每次操作会将序列的一段区间同时加上某个数,求出所有操作产生的新序列中字典序最小的一个。(\(1\le n,q\le 2\times 10^5\))
思路:
由区间加想到差分,要求序列字典序最小及要求其差分数组字典序最小。设当前答案为差分数组 \(dans\),其为第 \(p\) 次操作之后的差分数组,从第 \(p+1\) 此操作到当前操作的修改数组为 \(d\)(即当前差分数组为 \(a_i=dans_i+d_i\)),可以发现若 \(d\) 中的第一个不为 \(0\) 的数小于 \(0\),那么字典序就会变小。由于每一次修改只会修改 \(d\) 中的 \(l\) 和 \(r+1\),那么只需要用 set
维护这些修改的下标,即可 \(O(\log n)\) 查询第一个不为 \(0\) 的数即可。
代码:
https://codeforces.com/contest/1887/submission/274521362
反思:
看到区间加要想到差分(或线段树)。如果要求多次操作后的结果的最值,那么可以考虑每一次操作在满足什么条件下结果会变大或变小。
CF1887B Time Travel
题目链接:https://www.luogu.com.cn/problem/CF1887B
题意:
给定一张 \(n\) 个点的无向完全图和 \(t\) 组边集。每到达一个结点,你都必须至少等待 \(1\) 秒,才能继续前进。每条边的通过时间都是 \(0\) 秒。第 \(i\) 秒时,边集 \(a_i\) 中的边可以通行,其余边不能通行。求出从结点 \(1\) 到结点 \(n\) 的最少用时(不能到达则输出 -1)。(\(n,t,k,\sum m_i\le 2\times 10^5\))
思路:
考虑普通 dij,从结点 \(u\) 直接遍历可行边(包括不动),显然时间复杂度超标。但是我们发现很多的边其实都是从结点 \(u\) 走向节点 \(v\),只不过时间有先后,显然应该先走时间靠前的,并且要保证时间在结点 \(u\) 当前的时间之后,这可以先将每组边集的出现时间用 vector
存下来,然后二分解决。
代码:
https://codeforces.com/problemset/submission/1887/274545878
反思:
很多时候图论建模会超时间或空间,遇到明显是最短路,但是又有很多奇怪的限制的题目就要想到在普通 dij 的基础上修改。(如 CSP2023 T4)
at_abc364_f Range Connect MST
题目链接:https://atcoder.jp/contests/abc364/tasks/abc364_f
题意:
有一个 \(N+Q\) 个点的无向图,其中的点 \(N+i\) 会与 \(j\) 连边,其中 \(l_i\le j\le r_i\) 且 \(j\) 为整数。求这个无向图的最小生成树。(\(N,Q\le 2\times 10^5\))
思路:
考虑 kruskal 是如何求最小生成树,首先将边按照边权从小到大排序,然后遍历每一条边,若该边的两个端点不在一个连通块中就在最小生成树中加上这条边。考虑本题,本题应该先遍历 \(Q\) 点集的点,然后遍历 \([l_i,r_i]\),然后判断该不该加这条边,显然这样做会超时。我们可以发现任意两个区间最多相交一格,考虑建立 \(N\) 点集的并查集,每次遍历区间的时候可以将 j++
替换为 j = find(j)
,在循环内加上 merge(j,j+1)
,这样就可以跳过一部分,时间复杂度为 \(O(N+Q)\)。(但是要注意一些细节,比如:\(j\) 和 \(j+1\) 合并时要判断 \(j+1\) 是否小于等于 \(n\))
代码:
https://atcoder.jp/contests/abc364/submissions/56435514
反思:
看到这种模板题,但是给的条件具有规律时,我们要挖掘条件的性质,通常会与原算法进行对比,找到更优的解决方案。
at_abc364_g Last Major City
题目链接:https://atcoder.jp/contests/abc364/tasks/abc364_g
题意:
给定一个 \(n\) 个点,\(m\) 条边的带权无向图,设 \(\operatorname{f}(x)\) 为联通 \(1,2,\cdots,k-1,x\) 的原图的子图的最小总边权和,求 \(\operatorname{f}(k),\operatorname{f}(k+1),\cdots,\operatorname{f}(n)\)。(\(n\le 4000,n-1\le m\le 8000,k\le \min(n, 10)\))
思路:
最小斯坦纳树模板题。
容易发现子图一定是一颗树,否则可以删掉环上的一条边,使图仍然联通但是边权减少。考虑 \(f_{i,S}\) 表示以 \(i\) 为根的树包含点集 \(S\)(\(S\) 只包含 \(1,2,\cdots,k-1\))
考虑转移方程:对于不变的根 \(i\) 和两个集合 \(S,T\),那么有 \(f_{i,S\cup T}=\min(f_{i,S\cup T},f_{i,S}+f_{i,T})\)。我们发现若 \(S\cap T\neq\varnothing\),那么 \(f_{i,S}+f_{i,T}\) 一定不是最优的,所以我们可以枚举 \(S\cup T\) 和 \(S\)(注意 \(S\) 为 \(S\cup T\) 的子集),就可以算出 \(T\) 了。
在考虑不变的 \(S\) 如何转移 \(f_{i,S}\),先找到与 \(i\) 相邻的点 \(j\),很明显有 \(f_{i,S}=\min(f_{i,S},f_{j,S}+w(i,j))\)(\(w(i,j)\) 表示连接 \(i\) 和 \(j\) 的边的边权),虽然这个式子不满足拓补序,但是观察到它和最短路的转移很像,可以用最短路转移。
然后在考虑这两个转移的拓补序,前者需要对于每一个 \(S\) 需要知道 \(S\) 的子集的信息,所以先枚举集合与子集,然后用第一种转移,然后再枚举集合的同时用第二种转移即可。
答案既要包括 \(1,2,\cdots k-1\),又要包括 \(x\),那么输出 \(f_{x,\{1,2,\cdots k-1\}}\) 即可(\(k\le x\le n\))。
时间复杂度:\(O(3^k\cdot n+2^k\cdot m\cdot\log m)\)
枚举集合与子集的复杂度使 \(3^k\),这里简述一下,时间复杂度应为:
代码:
https://atcoder.jp/contests/abc364/submissions/56439613
P8190 [USACO22FEB] Cow Camp G
题目链接:https://www.luogu.com.cn/problem/P8190
题意:
Bessie 有一道题共有 \(T\) 个测试点,第一个测试点是样例,它一定通过该测试点,其中对于后 \(T-1\) 个测试点中的每一个测试点,Bessie 都有 \(\frac{1}{2}\) 的概率通过或不通过,一次提交的得分为其通过测试点的数量。Bessie 可以提交至多 \(K\) 次程序,定义 Bessie 这道题的得分为它最后一次提交的分数,请问 Bessie 的得分的最大期望是多少。
思路:
首先考虑 \(p_i\)(\(1\le i\le T\))为对于任意一次提交得分为 \(i\) 的概率,那么显然有 \({p_i=\dfrac{{T-1}\choose{j-1}}{2^{T-1}}}\),\(p_i\) 可以在 \(O(T)\) 内预处理求出。
设 \(f_i\) 表示 Bessie 提交了 \(i\) 次的得分的最大期望,那么有转移式:
简单讲一下这个式子的含义,枚举第 \(i\) 次的得分 \(j\),如果前 \(i-1\) 次的得分的最大期望比得分 \(j\) 更大,那么肯定不提交,否则就提交获得 \(j\) 的得分,再乘以得分为 \(j\) 的概率即可。
这样我们就会 \(O(T+K)\) 的做法了。
考虑优化。可以想到矩阵快速幂优化,但是 \(\max(f_{i-1},j)\) 不固定,所以不能直接矩阵快速幂。由于 \(\max(f_{i-1},j)\) 的取值很特殊,所以可以将 \(j\) 分为 \(j\le\lfloor f_{i-1}\rfloor\) 和 \(j\gt\lfloor f_{i-1}\rfloor\) 两段,接着我们可以将式子化成如下形式:
为了方便处理,我们设 \(g_i=\sum_{j=1}^i p_j,h_i=\sum_{j=i+1}^T(j\cdot p_j)\)(\(g_i,h_i\) 也可以 \(O(T)\) 预处理求出),那么:
这就更像矩阵乘法了,矩阵乘法的式子如下:
能够发现 \(f_{i-1}\in[0,T)\),那么 \(\lfloor f_{i-1}\rfloor\) 也只能有 \(T\) 个值,又由:
可知 \(f_i\) 单调递增,那么 \(\lfloor f_{i}\rfloor\) 单调不降,那么我们可以对于每一个 \(\lfloor f_i\rfloor\) 的值去做矩阵快速幂,也就是分段做矩阵快速幂。
那么如何寻找段与段之间的分界点呢,我们可以倍增。假设我们现在已经到了 \(j=\lfloor f_{i-1}\rfloor\) 这个值,不妨设此时的转移矩阵为 \(B\),我们可以先预处理出 \(B,B^2,B^4,\cdots B^{2^n}\)(\(2^n\le K\)),我们可以从 \(B^{2^n}\) 一直到 \(B\) 一直尝试,设当前矩阵为 \(F\),如果 \(F\times B^{2^i}\) 得到的期望值向下取整的结果为 \(j\),且还能过操作该次(即总操作次数小于等于 \(K\)),那么就将 \(F\gets F\times B^{2^i}\) 即可。
代码:
https://www.luogu.com.cn/record/173675684
反思:
如果遇到 dp 式中出现 \(\max(C,X)\)(\(C\) 为常数,\(X\) 为该转移的某个数),那么可以将 \(X\) 分段处理,去掉 \(\max\)。
如果遇到 \(\lfloor X\rfloor\) 或者 \(\lceil X\rceil\),且 \(\lfloor X\rfloor\) 或者 \(\lceil X\rceil\) 的取值个数不多可以看作一段一段时,可以考虑将每一段分段处理。例如:数论分块。