比赛记录(1~10)
1 2023年最后一哆嗦
1 得分
| 题目 | T1 | T2 | T3 | T4 | T5 | T6 | 总和 |
|---|---|---|---|---|---|---|---|
| 得分 | \(100\) | \(100\) | \(100\) | \(0\) | \(0\) | \(0\) | \(300\) |
排名:rank \(5\)。
2 题解
T1
根据题目所示的不等式,我们会得到:
\((2x+2y)n+x\le t<(2x+2y)n+(x+y)\)
\((p+q)m+p\le t <(p+q)m+(p+q)\)
也就是说,\(t\) 可以被写成 \((2x+2y)n+x+a(0\le a<y)\),同时也可以写成 \((p+q)m+p+b(0\le b <q)\)。
由此可得,\((2x+2y)n+x+a=(p+q)m+p+b\),即为不定方程 \((2x+2y)n-(p+q)m=p+b-x-a\)。
由于 \(y,q\) 的数据较小,因此我们可以枚举 \(a,b\),然后利用扩欧求解不定方程。
另解:
在枚举 \(a,b\) 时,根据上面不等式,会有:
\(\begin{cases}t\equiv x+a\pmod{2x+2y}\\t\equiv p+b\pmod{p+q}\end{cases}\)
对于这个线性同余方程组,采用 exCRT 求解即可。
T2
考虑先求出所有 \(n\) 范围内的素数,将其称为集合 \(P'\)。
又由于 \(\gcd(i,j)=p\) 可以转化为 \(\gcd(\dfrac ip,\dfrac jp)=1\)
因此原式可以转化为:
然后考虑换元,即令 \(\dfrac ip=q,\dfrac jp=r\),则会有:
然后处理这个式子就很模板了,最后结果为:
线性筛求解即可。
T3
最简单的一集。
考虑容斥原理,即 \(方案数=总方案数-不含\ 0\ 的方案数-不含\ 9\ 的方案数+不含\ 0\ 和\ 9\ 的方案数\),根据乘法原理易得结果为:
快速幂求解即可。
T4
首先想到的是贪心,但很明显不对。考虑 dp。
在结构体中记录下标和原数据,从小到大排序(因为小数的影响更小)。
接下来考虑 dp,设 \(dp(i,j)\) 表示将(排序后)的前 \(j - i + 1\) 个数全部放到区间 \([i,j]\) 里。
为什么这一段是对的呢?
考虑一种情况:设 \(a<b\),原序列为
***ab,则ba***要比ab***答案更优。因此这样做是正确的。
则对于当前一个数,放到两个端点之一肯定更大。
因此有状态转移方程:
初始状态就是 \(dp(i, i)=c[1]\times |id[1]-i|\),最终答案就是 \(dp(1,n)\)。
T5
首先很明显的是将每个数与 \(h\) 的差求出来,令 \(b_i = h - a_i\)。
接下来对于每一个数与前一个数求差,即考虑差分。令 $c_i = b_i - b_{i-1} $。
- \(|c[i]| > 1\) 时,由于左右端点不能重合,所以明显不成立,直接输出 \(0\);
- \(c[i] = 1\) 时,他必须作为一个新的左端点,左端点数量加一;
- \(c[i] = 0\) 时,他可以不进行操作,也可以既当左端点又当右端点,答案要乘上左端点数量加一;
- \(c[i] = -1\) 时,他必须与前面任意的左端点匹配,答案要乘上左端点数量,同时左端点数量减一;
于是我们记录左端点数量 \(p\),然后运用乘法原理计算答案即可 。
T6
简单的肯定是设 \(dp(i,j,k)\) 表示走了 \(k\) 步走到了点 \((i,j)\) 的方案数。但这个算法姑且不论时间,空间都开不下。
考虑一种新的状态,分别设:
-
\(dp(i,0)\) 表示第 \(i\) 步在终点;
-
\(dp(i,1)\) 表示第 \(i\) 步与终点同行(不在终点) ;
-
\(dp(i,2)\) 表示第 \(i\) 步与终点同列(不在终点) ;
-
\(dp(i,3)\) 表示第 \(i\) 步与终点没有关系;
则分别应有转移方程:
\(dp(i,0) = dp(i-1, 1) + dp(i-1, 2)\)
\(dp(i,1) = dp(i-1, 3) + dp(i-1, 1)\times (W - 2) + dp(i-1, 0) \times (W - 1)\)
\(dp(i,2) = dp(i-1, 3) + dp(i-1, 2) \times (H - 2) + dp(i-1, 0) \times (H - 1)\)
\(dp(i,3) = dp(i-1, 3) \times (H + W - 4) + dp(i-1, 1) \times (H - 1) + dp(i-1, 2) \times (W - 1)\)
答案就是 \(dp(k, 0)\)。
3 挂分
今日无挂分。
2 2024.2.6 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(30\) | \(80\) | \(20\) | \(100\) | \(230\) |
排名:rank \(5\)。
2 题解
T1
考虑统计区间的方式,想到双指针。
维护两个数组 \(L_i\) 和 \(R_i\)。\(L_i\) 表示以 \(i\) 为起点,使所有颜色合法的最小的一个 \(j\)。\(R_i\) 表示以 \(i\) 为起点,使所有颜色合法的最大的一个 \(j\)。注意有的 \(i\) 可能不存在这样的 \(j\),记其为 \(0\)。
容易发现 \(L_i,R_i\) 单调递增,考虑用双指针求出。
最后统计答案,如果有 \(L_i\le R_i\),则答案加上 \(R_i - L_i + 1\)。
T2
发现第一个操作是完全平推的。
维护一个数组 \(pos\),表示每次平推后 \(B\) 操作修改的位置,同时用 \(a\) 数组记录 \(B\) 操作修改的值。
这样可以在较小的复杂度内求出答案。
注意开 long long。
T3
依然是找区间,依然考虑双指针。
我们发现一个性质:如果区间 \([l,r]\) 的 \(\gcd\) 为 \(1\),那么 \([l,r+1],[l,r+2],\cdots,[l,n]\) 的区间 \(\gcd\) 都为 \(1\)。换句话说,对于一个区间 \(\gcd\) 为 \(1\) 的 \([l,r]\),他对答案的贡献是 \(n-r+1\)。
我们用双指针来模拟这个过程。
至于如何快速求区间 \(\gcd\),利用线段树即可。
T4
将字符串哈希变成数字,然后这道题就是 SDOI2009 HH的项链 这道题了。
当然还有很多其他做法,例如莫队、CDQ 分治等等。
3 挂分
- T2 操作完未清空 \(a\) 数组,\(100\rightarrow80\)。
3 2024.2.18 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(50\) | \(100\) | \(10\) | \(40\) | \(200\) |
排名:rank \(6\)。
2 题解
T1
Solution 1:
暴力分解所有数的质因数,对于 \(A,B\) 直接统计答案即可。
复杂度 \(O(n\sqrt{a_i})\)。
Solution 2:
对于每一个 \(a_i\),算出它与所有 \(b_i\) 的公约数。将他们都除掉这个公约数,并将答案乘上这个公约数。
复杂度 \(O(nm\log a_i)\)。
T2
Solution 1:
将贡献拆成横坐标和纵坐标。对于每一个坐标而言,比当前机器人坐标小的要被减去,大的要减去机器人坐标。我们先对两个数组分别排序,求出前缀和。然后二分查找出机器人的坐标,利用前缀和直接累加即可。
复杂度 \(O(m\log n)\)。
Solution 2:
仍然延续上面的思路,我们可以将所有坐标存进一个 BIT,这样对于每一个坐标区间查询即可。当然你用线段树也行,不过常数可能太大,会 T。
复杂度 \(O(m\log n)\)。
Solution 3:
预处理出在每一列(每一行)时,其左右(上下)以及这一列(一行)的点的个数。这样在询问坐标的时候可以直接 \(O(1)\) 回答。
复杂度 \(O(n\log n)\)。
T3
Solution 1:
一个 naive 的想法是,对于每一个字符串跑一遍 KMP,然后利用拓扑排序一次次向上递推,求出最后的结果。只能得到 \(10\) pts。
我们这个想法并没有错,但是在嵌套字符串的过程中,相邻两个字符串之间也有可能产生贡献。因此需要判断这个。
实现起来细节非常多,复杂度我也不知道。
Solution 2:
另外一个 naive 的想法是,直接暴力展开求出最后的字符串,暴力跑一遍 KMP 即可。可以得到 \(30\) pts。
我们依然考虑这个想法。考虑记忆化搜索。
设 \(dp(i,j)\) 表示当前准备匹配第 \(i\) 个文本串,模式串已经匹配到第 \(j\) 位时,将整个文本串匹配完后得到的模式串的数量。(请仔细注重定义)
我们每一位每一位扫描,如果是小写字母,那么就进行朴素的 KMP;如果是大写字母,就要往下进行搜索。
为了使搜索完之后可以返回模式串的匹配位置,我们在设一个数组 \(pos(i,j)\) 表示当前准备匹配第 \(i\) 个文本串,模式串已经匹配到第 \(j\) 位时,将整个文本串匹配完后模式串匹配到第几位。这样我们在返回的时候,直接将指针改为 \(pos(i,j)\) 即可。
最后跑一遍 DFS(F,0),结果就是 \(dp(F,0)\)。
复杂度 \(O(nl^2)\)。
T4
60 pts:
考虑设 \(dp(i,j,k)\) 表示当前枚举到了第 \(i\) 个数,最大公约数为 \(j\),选了 \(k\) 个数的方案数。方程为 \(dp(i,\gcd(a_i,j),k)=dp(i,\gcd(a_i,j),k)+dp(i-1,j,k-1)\)。
100 pts:
考虑容斥。互质数量 = 总数 - 不互质数量。设 \(num_i\) 表示数列中有多少个 \(i\) 的倍数。理论上讲,答案是 \(C_n ^4-\sum C_{num_i}^4\)。但这样会有重复,例如 \(num_2\) 与 \(num_4\)。同时如果 \(i\) 含有多个相同质因子,例如 \(num_8\),也会造成重复。因此去除他们后,答案应该是:
其中 \(cnt\) 表示 \(i\) 所含不同质数的数量。
这个式子求解的方式很多,暴力、线筛等等都可以。
复杂度 \(O(n\sqrt{a_i})\)。
3 挂分
- T1 中不小心将
num写成了cnt,\(100\rightarrow 50\)。
4 2024.2.19 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(100\) | \(20\) | \(30\) | \(250\) |
排名:rank \(4\)。
2 题解
T1
直接暴力枚举出所有小于 \(32767\) 的素数,求出前缀和。
然后做法就很多了,有预处理、双指针、二分等方法。复杂度就是 \(O(能过)\)。
T2
Solution 1:
设 \(dp(i,j,0/1)\) 表示第 \(i\) 天初始疲劳值为 \(j\) 时,第 \(i\) 分钟跑或不跑后,能跑的最长距离。
那么有状态转移方程:
暴力转移即可。初始状态 \(dp(0,0,0)=0\),结果为 \(dp(n+1,0,0)\)。
Solution 2:
设 \(dp(i,j)\) 表示第 \(i\) 天疲劳值为 \(j\) 能跑的最长距离。
则有状态转移方程:
暴力转移即可。
两者复杂度均为 \(O(nm)\)
T3
10 pts:
考虑暴力 DFS。直接枚举每一个位置放还是不放。
可以优化该算法,这样甚至能过掉样例:我们改为枚举每一列放多少个点,利用组合数计算即可。
100 pts:
首先要发现一个性质:对于在模 \(n\) 意义下相同的列 \(i\),当中放的点的个数都是相等的。这个容易证明。
也就是说对于点的个数来讲,他们每 \(n\) 个是一个循环节。
因此我们不必枚举所有的 \(m\),只需要枚举 \(n\) 即可。
考虑 dp。设 \(dp(i,j)\) 表示枚举到第 \(i\) 列,当前放了 \(j\) 个的方案数。那么有方程:
那么这样的复杂度是 \(O(n^4\log m)\),无法通过。
我们可以令 \(num(i,p)=(C_n^p)^{\frac mn+[i\le m\bmod n]}\),这样 \(O(n^2)\) 预处理出 \(num(i,p)\),复杂度就是 \(O(n^4)\)。可以通过。
T4
(p.s:此题我在考场上想出了正解,然而只剩最后 \(18\) 分钟了)
我们发现,只有删去的边是在最短路上的,才能让他迟到。
然而这也不是一定的,因为可能有多条最短路。
我们先跑一遍最短路,然后记录每个节点在最短路中可能的前置节点。这样我们就可以直接从终点跑一遍 DFS,建出一张新的“最短路图”。
在这个最短路图上,我们会发现,满足让他迟到的边一定是一条割边。因此我们再跑一遍 Tarjan 就可以求出答案。
个人感觉比 T3 简单(可能因为我 dp 太烂)。
3 挂分
今日无挂分。
4 2024.2.21 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(55\) | \(60\) | \(20\) | \(235\) |
排名:rank \(1\)。
2 题解
T1
首先我们可以推出一个式子:对于 \(a\le b\le c\le d\),一定有 \(ab+cd\ge ac+bd\ge ad+bc\)。
考虑将其推广到更高元,仍然成立。
根据这个结论,我们就可以直接来做这道题。将数列排序,前一半用大乘小,后一半相邻的数相乘。最后两者作差即为答案。
T2
看到 最小覆盖最大,直接可以想到二分答案。
二分最后的 size,然后考虑怎么检查。贪心是不行的,考虑 dp。
设 \(dp(i)\) 表示选到第 \(i\) 个位置上能得到的最大覆盖长度。直接由题意得状态转移方程为:
注意后一种是不可选的情况。
我们发现 \(j\) 在一段连续区间,因此可以单调队列优化 dp。
T3
首先考虑一个经典典中典的树上技巧:up and down。
具体的,设 \(f(i,0/1)\) 表示 \(i\) 的子树内选点的方案数,\(g(i,0/1)\) 表示子树外选点的方案数。
首先看 \(f(i,0/1)\),方程很简单:
接下来看 \(g(i,0/1)\),其实也很简单:
最后我们来看答案的式子。对于一条边,我们只需要统计两个点选或不选的总方案数即可。至于怎么求,利用 \(f(i,0/1),g(i,0/1)\) 即可,如下:
这是我们发现,公式中有除法,结果还要取余,因此只能用逆元求解。
T4
太难了,咕咕咕。
3 挂分
今日无挂分。
5 2024.2.22 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(70\) | \(100\) | \(40\) | \(80\) | \(290\) |
排名:rank \(5\)。
2 题解
T1
Solution 1:
暴力维护每一行滚动的上面的数,每一次判断是否有重复的情况,然后直接暴力计算循环节即可。
Solution 2:
容易发现每次横向滚动以 \(4\) 次为一个周期,因此我们每一行只需要维护 \(C\bmod4\) 的情况即可。
T2
30 pts:
容易发现这是二分图匹配,暴力计算即可。
100 pts:
首先容易证明,对于一个男生要选个子比他高的,选尽可能低的那一个对于后面的男生更有利。
因此我们考虑贪心,将两种人分开考虑,一个升序一个降序排列,利用贪心求解即可。
T3
首先我们发现,最小生成树的边权之和是 \(n-1\),因为 \(1\) 和所有点都有连边。
因此所有最小生成树的边权和都是 \(n-1\),也就是说,最小生成树上的父子一定互质。
又因为父亲的编号小于儿子的编号,因此一个点的父亲的方案数就是小于他且与他互质的数的个数。显然这就是欧拉函数的定义。
因此最后答案就是 \(\prod\limits_{i=1}^n\varphi(i)\)。
T4
折半搜索的模板题。
考虑爆搜的复杂度是 \(O(2^n)\),过不了 \(n=40\)。
我们考虑将区间分成两段,这样复杂度就是 \(O(2^{\frac n2})\),能够通过。
我们记录下两段区间分别可以凑出的数 \(p_i,q_i\),然后我们要找出两个序列中的一对数,使得他们的和接近 \(m\)。
显然直接枚举 \(p_i\),然后在 \(q\) 中二分查找即可。
3 挂分
- T1 移动骰子时的转移写错,\(100\rightarrow 70\)。
- T3 忘记了 \(\varphi\) 函数,导致离正解只有一步之遥。
6 2024.3.10 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(80\) | \(60\) | \(30\) | \(45\) | \(215\) |
排名:rank \(7\)。
2 题解
T1
此题就是一个模意义下的背包,设 \(dp(i,j)\) 表示选了 \(i\) 个数,总和余数为 \(j\) 的方案数。那么由方程:
暴力计算即可。
T2
又是典中典的 up and down。
第一遍 DFS 可以直接维护子树内最长链,然后在第二个 DFS 中求出子树外最长链。但是这样会有一个问题,如果在第二个 DFS 时,这个节点就是父节点的最长链上的节点,那么就不能简单的用最长链更新。解决方法也很简单再记录一个次长链即可。
记子树内最长链为 \(f_i\),子树内次长链为 \(g_i\),子树外最长链为 \(p_i\)。因此,当该节点是父节点最长链上的点时,\(p_i=\max(g_{fa},g_{fa})+1\),否则 \(p_i=\max(f_{fa},g_{fa})+1\)。
T3
首先列出这样一个显然的式子:\(C(i)=C(i-1)10^p+i\)。其中 \(p\) 为 \(i\) 的位数。
显然 \(O(n)\) 递推无法满足,我们考虑优化。这种齐次线性递推显然可以矩阵快速幂优化。但是我们要将 \(i\) 拆成 \(i-1+1\)。最后的矩阵是:
其中,我们可以直接枚举 \(p\) 进行计算。
T4
首先,\(k=8\),显然考虑状压。考虑设 \(dp(i,j,s)\) 表示枚举到 \(i\) 个点,共连了 \(j\) 条边,从 \(i\sim i-k\) 的点的度的奇偶状态(\(0\) 为偶,\(1\) 为奇)。
但是我们会发现这样不好实现,因此在加入一维。设 \(dp(i,j,s,l)\) 表示枚举到 \(i\) 个点,共连了 \(j\) 条边,从 \(i\sim i-k\) 的点的度的奇偶状态,当前只考虑 \(i\sim i-k+l\) 的方案数。
接下来我们分析。
如果现在不连 \(i\to i-k+l\) 这条边,那么 \(dp(i,j,s,l + 1 )=\sum dp(i,j,s,l)\)。
如果现在要连 \(i\to i-k+l\) 这条边,那么 \(dp(i,j+1,s\oplus2^k\oplus2^l,l+1)=\sum dp(i,j,s,l)\)。
最后我们考虑转移到下一个点,只有当 \(s\) 的最后一位,即 \(i-k\) 的度时偶数时才可以转移,因为从此以后再也不会更新 \(i-k\) 了。
此时方程为 \(dp(i+1,j,s\) >> \(1,0)=\sum dp(i,j,s,k)\)。
初始化是 \(dp(2,0,0,0)=1\),答案是 \(dp(n+1,m,0,0)\)。
3 挂分
- T1 写了一个非常傻逼的 \(O(n^2k)\) 复杂度,还没发现。
- T2 又差一步推出 up and down。
7 2024.3.24 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(90\) | \(80\) | \(0\) | \(270\) |
排名:rank \(3\)。
2 题解
T1
首先求出连通块,暴力 BFS 即可。
记录“四至点”,然后看暴力到的点数和矩形的点数是否相同即可。
T2
首先,这道题的答案就是概率。每个数列的贡献为 \(1\),因此期望等于概率。
记 \(N=\sum\limits_{i=1}^7a_i\),接下来推概率公式,对于选 \(7\) 个数按顺序构成 \(1\sim 7\) 的概率是:
然而帕琪七重奏只需要构成 \(1\sim 7\) 排列即可,因此方案数为:
现在计算在整个长为 \(N\) 的数列中出现一次帕琪七重奏的概率。由于一个数列有 \(N-6\) 个位置可以放下帕琪七重奏,所以将上面概率乘上 \(N-6\) 即可。最终答案为:
T3
本题按照部分分做基本可以拿到满分。
最开始的一步:发现 \(n<m\),因此我们尽可能枚举 \(n\)。考虑到可以将 \([1,n]\) 值域内的答案全部计算出来,再对于 \(m\) 查询即可。
\(O(n^4)\) 40 pts:
直接暴力枚举 \(X_a,X_b,X_c,X_d\) 即可。
\(O(n^4)\) 55 pts:
发现由于 \(X_a<X_b<X_c<X_d\),因此每次都可以在上一个数加一的地方开始枚举。
\(O(n^3)\) 85 pts:
从这里开始就是提示正解了。
首先,如果确定 \(X_d-X_c=t\),那么显然 \(X_b-X_a=2t\)。
由于有 \(X_b-X_a<\dfrac 13 (X_c-X_b)\),就可以得到 \(2t<\dfrac 13(X_c-X_b)\),即 \(X_c-X_b>6t\)。
我们再记 \(X_c-X_b=6t+k(k> 0)\)。
此时枚举 \(X_a,t,k\),就可以计算出 \(X_b,X_c,X_d\)。
我们记 \(t(x)\) 为一个桶,那么对于这四个数字,每个数字对应的答案都应该乘上其他三个数字的 \(t\) 值。例如 \(ans_{X_a}=t(X_b)\times t(X_c)\times t(X_D)\)。
\(O(n^2)\) 100 pts:
考虑对上面的算法再进行一些优化。
如果我们考虑只枚举 \(D,t\),是否可行呢?
此时 \(C\) 好求,为 \(D-t\)。然而 \(k\) 未知,似乎求不出 \(A,B\)。
此时我们发现,令 \(k=1\) 即可得到 \(A,B\) 的最大值。同时我们还发现,对于小于这个最大值 \(A,B\) 的数 \(A',B'\) 也是满足要求的!
因此我们可以在正序枚举 \(D\) 的时候记录 \(t(A)\times t(B)\) 的前缀和,这样就可以求出 \(anc_{C}\) 以及 \(ans_{D}\)。
然后再倒序枚举 \(A\),记录 \(t(A)\times t(B)\) 的后缀和,就能求出 \(ans_A\) 以及 \(ans_B\) 了。
T4
首先看到这个题目的描述自然会想到区间 dp。
然后我们又发现 \(k\le 8\),并且还只有 \(01\) 串,因此还要进行状压 dp。
考虑如何设计状态。设 \(dp(i,j,S)\) 为将区间 \([i,j]\) 最终合并为状态 \(S\) 的最大得分。
我们枚举断点 \(mid\),此时我们希望右边的区间可以 合并为一个字符,那么 \(mid\) 就只能等于 \(j,j-k+1,j-2k+1\cdots\)。此时会有状态转移方程:
其中 \(\times 2\) 就是左移一位。
但是还有一种特殊的情况,即如果整个区间本身就能合并为一个字符的情况。那么我们又会有转移方程:
但是又有一个问题,当 \(c(S)=S\) 的时候 dp 数组会自我更新,因此还要用一个辅助数组计算后再赋值即可。
3 挂分
今日无挂分。
8 2024.4.5 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(28\) | \(0\) | \(0\) | \(100\) | \(128\) |
排名:rank \(5\)
2 题解
T1
首先,根据约数和定理,一个数的正约数之和为 \(\prod\limits_{i=1}\sum\limits_{j=0}^{a_i}{p_i}^j\)。而要求这个正约数之和为 \(S\)。
显然枚举是不好实现的,但是我们还有一个强有力的工具——爆搜。
我们在一层内枚举 \(\sum\limits_{j=0}^{a_i}{p_i}^j\),同时累乘 \({p_i}^{a_i}\)。将 \(S\) 除掉 \(\sum\limits_{j=0}^{a_i}{p_i}^j\) 后进入下一层继续搜索。
然后剩下的就是细节:
- 我们无法枚举 \(10^9\) 之内所有质数,枚举到 \(10^5\) 左右即可。剩下的质数需要单独特判。
- 注意循环的时间复杂度,需要优化。
T2
一道比较简单的概率。
首先我们肯定是设 \(f_i\) 表示取到 \(i\) 张邮票后还要付的期望钱数。然而你会发现这推不出什么。
问题在于这道题的钱数和次数是相关的,而且次数也不容易求出。
因此我们再设 \(g_i\) 表示取到 \(i\) 张邮票后还要取的期望次数。那么这个还算好求,分类讨论即可:
- 当这一张以前抽到过,则 \(g_i=\dfrac in (g_i+1)\);
- 当这一张以前没有抽到,则 \(g_i=\dfrac{n-i}n(g_{i+1}+1)\);
二者相加即可。但是此时右边还有 \(g_i\),于是移项后得:
那么有了 \(g\) 数组,\(f\) 数组就好求一些了。仍然分类讨论:
- 当这一张之前抽到过,则 \(f_i=\dfrac in(f_i+g_i+1)\);
- 当这一张以前没有抽到,则 \(f_i=\dfrac {n-i}n(f_{i+1}+g_{i+1}+1)\);
这个式子看起来有点鬼畜,解释一下:以第二个为例,\(f_{i+1}\) 是显然的,而此时又拿了一个,于是第 \(i+1\) 次往后的所有取了的邮票的钱数都要加一(相当于他们被取的次数增加了),然后再加上第 \(i\) 个这一个 \(1\),就得到了这个式子。
此时仍然需要移项,后得:
暴力计算两个数组即可。
T3
个人认为最难的一道题。
首先关注构成的树的总个数,显然是 \(n!\) 个。于是原题其实就是求最后的路径之和。
接着我们考虑如何计算路径之和。这里采用非常典的拆贡献,对于每条边,走过它的次数就是 \(2\text{siz}_i(n-\text{siz}_i)\)。但是这样加起来每条边都多算了一次,因此一条边的走过次数就是 \(\text{siz}_i(n-\text{siz}_i)\)。
接下来考虑如何求总的结果。此时我们并不需要具体求出 \(\text{siz}\),这需要知道每一种 \(\text{siz}\) 对应的方案数即可。
下面先看一段证明:
考虑这样一件事:对于一个点,其子树大小为 \(k\),那么还会有多少个剩下的位置可以放?
引理:每多放一个点,就会多一个位置。
证明:显然。对于放下的那个位置,会使得位置减一。但同时这个点的两个儿子又使得位置加二。因此位置加一。
于是对于上面的问题,答案就显然为 \(k+1\)。
同时如果子树外有 \(k\) 个点,那么位置就是 \(k+1-1=k\)。
此时我们设 \(dp(i,j,k)\) 表示当前子树根节点为 \(i\),枚举到 \(j\) 个点,\(i\) 的子树大小为 \(k\) 时的方案数。分类转移:
- 当 \(j\) 号点没有放在 \(i\) 的子树内,则此时剩下的位置数量就是 \(j-1-k\),于是方案数为 \(dp(i,j-1,k)\times (j-1-k)\)。
- 当 \(j\) 号点放在 \(i\) 的子树内,则此时剩下的位置就是 \(k\),于是方案数为 \(dp(i,j-1,k-1)\times k\)。
综合起来得:
此时我们发现一个严重的问题,这样的复杂度是 \(O(n^3)\)。不过又可以发现,转移方程与 \(i\) 无关,因此将 \(i\) 合并,只保留 \(dp(j,k)\) 即可。
接下来考虑初值,显然有 \(dp(1,k)=k!\)。
最后我们将方案数乘上边的次数即可,答案就是 \(\sum\limits_{i=1}^ndp(n,i)\times i\times (n-i)\)。
T4
正常想法:
首先考虑转化为每条边期望经过的次数,对于一段区间,显然是 \(\sum\limits_{i=l}^r(i-l+1)(r-i+1)\),于是总的期望就是 \(\sum\limits_{i=l}^rv_i(i-l+1)(r-i+1)\)。
将其拆开得 \(\sum\limits_{i=l}^r i\cdot v_i\times (r+1)-i^2\cdot v_i-v_i\times(l-1)(r+1)+i\cdot v_i\times(l-1)\)。
由于题目中提到了要区间修改,区间查询,那么我们首先想到线段树。
再考虑上面的式子,我们只需要维护 \(v_i,i\cdot v_i,i^2\cdot v_i\)。
但是这样我们前面的系数不能随着区间来变化,因此我们实际处理中还要进行一些加减。
非正常想法:
首先,打表出每一个区间长度下每一个收费站期望经过的次数:
\(1\)
\(2,2\)
\(3,4,3\)
\(4,6,6,4\)
\(5,8,9,8,5\)
不难发现第 \(i\) 列都是顺次 \(i\) 的倍数。
因此我们可以写出通用的式子:\(\sum\limits_{i=l}^rka_l+2(k-1)a_{l+1}+3(k-2)a_{l+2}+\cdots+(r-l+1)(k-r+l)a_r\)。
对于这个式子,展开处理,后面就与正常做法相同了。
3 挂分
今日无挂分。
9 2024.5.2 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(36\) | \(100\) | \(10\) | \(246\) |
排名:rank \(2\)
2 题解
T1
大模拟题。
直接按照题意维护即可。利用栈判断循环的开始和结束,同时统计复杂度。注意 \(x>y\) 时不计算内部的复杂度。
T2
分层赋分题。
将题目看做在矩阵中选点,则每一行只能有一个点。
36pts: 暴力枚举每一行的点在第几列(或不选),判断即可。复杂度 \(O(m^n)\)。
64pts:
考虑到 \(m\) 较小,因此可以作为 dp 状态。设 \(dp(i,j,k,l)\) 表示前 \(i\) 行中第 \(1\) 列放了 \(j\) 个,第 \(2\) 列放了 \(k\) 个,第 \(3\) 列放了 \(l\) 个的方案数。
那么显然会有状态转移方程 :
最后答案为 \(\sum\limits_{\max(j,k,l)\le \frac{j+k+l}{2} \cap j+k+l\ge 0} dp(n,j,k,l)\),复杂度 \(O(n^{m+1})\)。
到此就是暴力方法能够拿到的最好分数了。下面开始正解。
84 pts:
考虑到如果不合法,那么只可能有一列超出总数的一半。
而这样就意味着不合法情况是好求的,那么可以考虑容斥,用总方案数 - 不合法方案数。
先看总方案数。每一行可以任意选一个或者不选,令 \(S_i=\sum\limits_{j=1}^m a(i,j)\),那么总方案数就是 \(\prod\limits_{i=1}^m(S_i+1)\)。但是我们不能选空集,因此还要减一。
接下来是不合法方案数。由于不合法的列只可能有一个,我们就先枚举它。设当前枚举到的列为 \(c\)。
仍然考虑 dp,设 \(dp(i,j,k)\) 表示前 \(i\) 行中,第 \(c\) 列总共放了 \(j\) 个,其他列总共放了 \(k\) 个的方案数,那么就有转移方程:
最后答案为 \(\sum\limits_{j>k}dp(n,j,k)\)。复杂度是 \(O(mn^3)\) 的,仍然不够优秀。
100pts:
考虑到上面的步骤中哪里最浪费时间,显然是 dp。我们发现我们的最终答案对于 \(j,k\) 的具体数值并不关心,只关心 \(j>k\) 这个条件。
那么我们便可以将后两维合并成 \(j-k\),枚举 \(j-k\) 即可。这样我们就会得到最后的转移方程(\(dp(i,p)\) 表示前 \(i\) 行 \(j-k=p\) 的方案数):
最后答案为 \(\sum\limits_{p>0}dp(n,p)\),复杂度 \(O(mn^2)\),可以通过。
T3
首先发现 \(n\le 14\),因此立刻想到状压。
我们设 \(dp(i,S)\) 表示深度为 \(i\) 时总共建立的地铁状态 \(S\) 时的最小花费,那么显然有方程:
这里 \(S-T\) 表示从 \(S\) 集合中去掉 \(T\),\(cost(i,T)\) 表示在深度为 \(i\) 的地方修建地铁方案为 \(T\) 的花费。
那么显然我们现在要解决两个问题:\(cost\) 和 \(dp\) 的求解。
先看 \(cost\),我们想通过预处理求解。
不妨再设一个数组 \(sub(i,j)\),表示地铁 \(i\) 修建在深度为 \(j\) 的地方的花费。显然求解 \(sub\) 数组是 \(O(n^2m)\) 的。
接下来考虑 \(cost\),我们不能完全利用 \(sub(i,j)\) 求解,因为还需要判断合法。此时再再设一个数组 \(flg(i,j)\),表示第 \(i\) 条地铁能否与第 \(j\) 条地铁在同一深度修。显然这个数组也是可以 \(O(n^2m)\) 求解的。
那么 \(cost\) 此时就可以求出来了,判断合法并求和,复杂度是 \(O(n^32^n)\)。
接下来考虑求解 dp 数组,按照朴素的算法我们需要枚举两次子集,这样算是 \(O(n2^n\cdot 2^n)=O(n4^n)\) 的,十分不优秀。
我们考虑下面这种奇妙的技巧:
for (int T = S; T; T = (T - 1) & S)
感性证明:
我们其实可以直接观察 T 的变化,立马就能理解。设 \(S=(10110)_2\),那么 T 的二进制变化就如下:
\(10110\to 10100\to 10010\to 10000\to00110\to 00100\to 00010\to 00000\)。
可以观察到枚举是没有浪费且不重不漏的。
复杂度说明:
首先我们在该循环的外层枚举了 \(S\),而内层本质上是选出含有 \(1\) 的子集。设总共有 \(k\) 个 \(1\),则内层的复杂度就是 \(O(2^k)\)。
考虑到外层实际上就是在 \(n\) 个数中随便选 \(1\),因此总的复杂度我们可以写成 \(\sum\limits_{k=0}^nC_n^k\times 2^k\)。那么我们改写成 \(\sum\limits_{k=0}^nC_n^k\times 2^k\times 1^{n-k}\)。根据二项式定理可得这就是 \((1+2)^n\),即 \(3^n\)。
因此这一部分的复杂度其实是 \(O(3^n)\)。
那么通过上面的分析,我们就通过这样的优化将复杂度优化至 \(O(n3^n)\),可以压线通过了。
最后复杂度是 \(O(2n^2m+n^32^n+n3^n)\),不是很能跑满所以压线能过。
T4
我们可以想到用子树来更新当前的值。假如当前节点是 \(u\),有一个儿子 \(v\)。\(val_v\) 的值是 \((v_{c_1} + d(c_1, x)) \oplus (v_{c_2} + d(c_2, x)) \oplus \cdots \oplus (v_{c_k} + d(c_k, x))\),那么由于 \(u\) 是 \(v\) 的父亲,所以此时所有的 \(d\) 都会加一。因此 \(val_u\) 的一部分应该是 \((v_{c_1} + d(c_1, x)+1) \oplus (v_{c_2} + d(c_2, x)+1) \oplus \cdots \oplus (v_{c_k} + d(c_k, x)+1)\)。
我们将 \(u\) 的所有儿子 \(v\) 的信息合并到自己,给每一项加一求出异或和,最后再异或上自己的 \(v_u\) 就可以得到 \(val_u\) 了。
考虑到这里面有位运算操作,还要进行一系列合并维护,想到利用 01-Trie。
此时我们将上面的操作总结起来就是在 01-Trie 上进行全局加一、插入、维护异或和、合并。
我们先看全局加一,这关系到 Trie 的形态。
我们在二进制下举一些例子,观察加一后的变化:\((100)_2+1=(101)_2,(10011)_2+1= (10100)_2,(11111)_2+1=(100000)_2,(1000001111111101111111)_2+1=(1000001111111110000000)\)。
我们发现,二进制下加一就是将二进制末尾连续的 \(1\) 变成 \(0\),同时将从左往右的第一个 \(0\) 换成 \(1\)。
这就启发我们 01-Trie 应该是从低位向高位建。接下来考虑具体如何在 Trie 上加一。
我们发现,无论怎么样,都是要将 \(0\) 变为 \(1\)、\(1\) 变为 \(0\),这样的操作放到 Trie 上就是交换左右儿子。接下来我们还需要继续处理原先是 \(1\) 的部分(也就是交换后 \(0\) 的部分),而原先是 \(0\) 的部分现在就完成了加一操作,不需要再动了。
这样做会发生进位,因此在插入的时候我们需要给前面填上几个 \(0\),保证不会超出范围。
对于维护异或和,我们考虑从 Trie 树上子树的信息转移过来。对于 \(0\) 的儿子,我们直接左移一位即可。对于 \(1\) 的儿子,左移一位后还需要进一步处理。我们发现 \(1\) 这条边会经过很多次,而只有经过次数是奇数的时候我们才能给当前值加一。因此还需要维护每一条边经过次数。
最后就是合并,我们可以将 Trie 看成一颗动态开点线段树,按照一般方式直接合并即可。这样的思想也可以说明上面维护异或和的方法:既然可以看做线段树,我们就可以进行 pushup 操作,也就是上面用子树信息更新。
最后我们在原树上进行 DFS,利用 01-Trie 维护即可。
3 挂分
今日无挂分。
10 2024.5.3 测试
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(51\) | \(45\) | \(30\) | \(226\) |
排名:rank \(7\)。
2 题解
T1
我们发现取出队列元素之后可以任意放置,那么我们就可以把在当前 \(x\) 之上的要送出的礼物重新排列,让他们按顺序排在栈顶,这样每次取出都只花费 \(1\) 时间。当要送出礼物在 \(x\) 下的时候再重新计算即可。
形式化的讲,我们给每一个要送出的礼物在栈中的下标记为 \(p_i\)。那么对于一个 \(i\),我们找到它右边第一个满足 \(p_j>p_i\) 的 \(j\),则 \((i,j)\) 区间内的礼物都是在 \(i\) 之上的,经过排列每一次取出礼物都只需要 \(1\) 单位时间。
这样我们只是对于每一个礼物进行遍历,复杂度 \(O(\sum m)\)。
T2
首先考虑暴力。修改操作是 \(O(1)\) 的,关键在于查询。
我们令每一位上最后可能得取值为 \(p_i\),则答案就是 \(\prod p_i\)。
关注 \(p_i\) 的求法。如果这一位上 \(0\) 和 \(1\) 同时存在,显然不满足,\(p_i=0\);如果这一位上只出现 \(0\) 或 \(1\) 以及若干个 \(2\)(可以不出现),那么 \(p_i=1\);如果全部是 \(2\),那么 \(p_i=2\)。
这样做是 \(O(qnm)\) 的,不够优秀。
我们首先发现这样一件事:在求出 \(p_i\) 的时候,我们其实只需要求出这一位上有多少个 \(0\) 和 \(1\) 即可求出答案。那么现在我们把整个字符的矩阵转过来看,就是在一个区间上修改一位,同时求出区间上 \(01\) 个数的和。这显然可以使用线段树维护。
那么总共有 \(n\) 位,正好 \(n\) 只有 \(30\),因此是可以开的下的。复杂度 \(O(qn\log m)\)。
但是我们发现这样做运算量达到了 \(5e8\),因此还要卡常。将线段树换成树状数组就行了。
T3
玄妙。
50pts:
我们可以将整道题看做分组背包。我们可以将每一列看做一个组,耗费的空间就是打的次数,价值就是所得分数。
这样做看似非常合理,但是实际上没有任何道理。我们在打 Y 砖块的时候固然可以看做没有消耗子弹,但是当我们的子弹数量是 \(0\) 的时候,照样打不了 Y 砖块。因此该做法是错误的。
然而由于前 50pts 没有 Y,因此可以水过。
100pts:
我们首先来考虑一个借子弹的思想:
将第 \(i\) 列的子弹借给第 \(j\) 列,其实就是调换了顺序。
我们本来是先打 \(i\) 再打 \(j\),但是我们可以在打 \(i\) 的时候让自己的手中空下几发子弹,给 \(j\) 列先打。在 \(j\) 列打的过程中可能打到 Y 砖块,这样它就会保留这几发子弹,然后再还给 \(i\) 打。
这样我们消耗的子弹数量一致,而且在保证打 Y 砖块时有子弹的前提下获得了更多分数。
现在我们回来看这道题。仍然考虑 dp,设 \(dp(i,j,0)\) 表示在前 \(i\) 列打了 \(j\) 发子弹,最后一发子弹打的是 N 砖块获得的最高分数;\(dp(i,j,1)\) 表示在前 \(i\) 列打了 \(j\) 发子弹,最后一发子弹打的是 Y 砖块获得的最高分数。
同时我们还需要两个辅助数组,设 \(v(i,j,0)\) 表示在第 \(i\) 列打了 \(j\) 发子弹,最后一发子弹打的是 N 砖块获得的最高分数;\(v(i,j,1)\) 表示在第 \(i\) 列打了 \(j\) 发子弹,最后一发子弹打的是 Y 砖块获得的最高分数。
现在考虑转移方程。我们先看 \(dp(i,j,1)\)。
我们根据上面的分析,在打 \(i\) 列上的 Y 砖块前必须要保证有一发子弹,也就意味着打完 Y 之后会剩下一发子弹。这就告诉我们在 \(dp(i,j,1)\) 这个状态,手中肯定会剩下一发子弹。那么假设我们当前要打的子弹数是 \(k\),我们在上一次打的时候就要打 \(j-k\) 次并且还要剩一发子弹。显然直接是 \(dp(i-1,j-k,1)\)。此时在加上打 \(k\) 次的分数就可以得到:
接下来是 \(dp(i,j,0)\)。
首先我们显然会有 \(dp(i,j,0)=\max\{dp(i-1,j-k,0)+v(i,k,0)\}\)。但是根据我们上面借子弹的理论,我们是可以多打几个 Y 砖块的。接下来分类讨论一下借子弹的对象:
- 当 \(i\) 给前 \(i-1\) 列借
此时我们最后一发打到的是 \(i\) 上的 N 砖块。假设我们当前要打的子弹数是 \(k\),我们在上一次打的时候就要打 \(j-k\) 次。我们在打完前 \(k-1\) 发之后,将剩下的这一发子弹借给前面的列,这样前面的列就可以打 Y 砖块。在打完 Y 砖块后会剩下一发子弹,再还给第 \(i\) 列就可以打完了。
于是我们的转移方程就是:
- 当前 \(i-1\) 列给第 \(i\) 列借。
同理,这样最后一发打到的是前 \(i-1\) 列。我们在打完前 \(k-1\) 发后把子弹借给 \(i\) 列,让 \(i\) 列多打几个 Y 砖块,打完后把子弹还回去即可。转移方程为:
然后我们直接转移即可。最后答案为 \(dp(m,k,0)\),因为如果最后一发打到的是 Y,那么就一定会多出一发子弹,肯定不优。
时间复杂度 \(O(nmk)\)。
T4
爆搜即可。
具体来讲,我们只需要判断顺子和带牌,剩下的我们都可以看成散牌,然后直接统计即可。
3 挂分
- T2 动态开点线段树空间开小,\(60\to 51\)。

浙公网安备 33010602011771号