比赛记录(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+xt<(2x+2y)n+(x+y)

(p+q)m+pt<(p+q)m+(p+q)

也就是说,t 可以被写成 (2x+2y)n+x+a(0a<y),同时也可以写成 (p+q)m+p+b(0b<q)

由此可得,(2x+2y)n+x+a=(p+q)m+p+b,即为不定方程 (2x+2y)n(p+q)m=p+bxa

由于 y,q 的数据较小,因此我们可以枚举 a,b,然后利用扩欧求解不定方程。

另解:

在枚举 a,b 时,根据上面不等式,会有:

{tx+a(mod2x+2y)tp+b(modp+q)

对于这个线性同余方程组,采用 exCRT 求解即可。

T2

考虑先求出所有 n 范围内的素数,将其称为集合 P

又由于 gcd(i,j)=p 可以转化为 gcd(ip,jp)=1

因此原式可以转化为:

pPi=1nj=1n[gcd(ip,jp)=1]

然后考虑换元,即令 ip=q,jp=r,则会有:

pPq=1npr=1np[gcd(q,r)=1]

然后处理这个式子就很模板了,最后结果为:

pP((q=1np2φ(i))+1)

线性筛求解即可。

T3

最简单的一集。

考虑容斥原理,即 = 0  9 + 0  9 ,根据乘法原理易得结果为:

10n2×9n+8n

快速幂求解即可。

T4

首先想到的是贪心,但很明显不对。考虑 dp。

在结构体中记录下标和原数据,从小到大排序(因为小数的影响更小)。

接下来考虑 dp,设 dp(i,j) 表示将(排序后)的前 ji+1 个数全部放到区间 [i,j] 里。

为什么这一段是对的呢?

考虑一种情况:设 a<b,原序列为 ***ab,则 ba*** 要比 ab*** 答案更优。

因此这样做是正确的。

则对于当前一个数,放到两个端点之一肯定更大。

因此有状态转移方程:

dp(i,j)=max{dp(i+1,j)+c[i]×|id[i]i|,dp(i,j1)+c[i]×|id[i]j|}

初始状态就是 dp(i,i)=c[1]×|id[1]i|,最终答案就是 dp(1,n)

T5

首先很明显的是将每个数与 h 的差求出来,令 bi=hai

接下来对于每一个数与前一个数求差,即考虑差分。令 ci=bibi1

  1. |c[i]|>1 时,由于左右端点不能重合,所以明显不成立,直接输出 0
  2. c[i]=1 时,他必须作为一个新的左端点,左端点数量加一;
  3. c[i]=0 时,他可以不进行操作,也可以既当左端点又当右端点,答案要乘上左端点数量加一;
  4. 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(i1,1)+dp(i1,2)

dp(i,1)=dp(i1,3)+dp(i1,1)×(W2)+dp(i1,0)×(W1)

dp(i,2)=dp(i1,3)+dp(i1,2)×(H2)+dp(i1,0)×(H1)

dp(i,3)=dp(i1,3)×(H+W4)+dp(i1,1)×(H1)+dp(i1,2)×(W1)

答案就是 dp(k,0)

3 挂分

今日无挂分。

2 2024.2.6 测试

1 得分

题目 T1 T2 T3 T4 总分
得分 30 80 20 100 230

排名:rank 5

2 题解

T1

考虑统计区间的方式,想到双指针。

维护两个数组 LiRiLi 表示以 i 为起点,使所有颜色合法的最小的一个 jRi 表示以 i 为起点,使所有颜色合法的最大的一个 j。注意有的 i 可能不存在这样的 j,记其为 0

容易发现 Li,Ri 单调递增,考虑用双指针求出。

最后统计答案,如果有 LiRi,则答案加上 RiLi+1

T2

发现第一个操作是完全平推的。

维护一个数组 pos,表示每次平推后 B 操作修改的位置,同时用 a 数组记录 B 操作修改的值。

这样可以在较小的复杂度内求出答案。

注意开 long long

T3

依然是找区间,依然考虑双指针。

我们发现一个性质:如果区间 [l,r]gcd1,那么 [l,r+1],[l,r+2],,[l,n] 的区间 gcd 都为 1。换句话说,对于一个区间 gcd1[l,r],他对答案的贡献是 nr+1

我们用双指针来模拟这个过程。

至于如何快速求区间 gcd,利用线段树即可。

T4

将字符串哈希变成数字,然后这道题就是 SDOI2009 HH的项链 这道题了。

当然还有很多其他做法,例如莫队、CDQ 分治等等。

3 挂分

  • T2 操作完未清空 a 数组,10080

3 2024.2.18 测试

1 得分

题目 T1 T2 T3 T4 总分
得分 50 100 10 40 200

排名:rank 6

2 题解

T1

Solution 1:

暴力分解所有数的质因数,对于 A,B 直接统计答案即可。

复杂度 O(nai)

Solution 2:

对于每一个 ai,算出它与所有 bi 的公约数。将他们都除掉这个公约数,并将答案乘上这个公约数。

复杂度 O(nmlogai)

T2

Solution 1:

将贡献拆成横坐标和纵坐标。对于每一个坐标而言,比当前机器人坐标小的要被减去,大的要减去机器人坐标。我们先对两个数组分别排序,求出前缀和。然后二分查找出机器人的坐标,利用前缀和直接累加即可。

复杂度 O(mlogn)

Solution 2:

仍然延续上面的思路,我们可以将所有坐标存进一个 BIT,这样对于每一个坐标区间查询即可。当然你用线段树也行,不过常数可能太大,会 T。

复杂度 O(mlogn)

Solution 3:

预处理出在每一列(每一行)时,其左右(上下)以及这一列(一行)的点的个数。这样在询问坐标的时候可以直接 O(1) 回答。

复杂度 O(nlogn)

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(nl2)

T4

60 pts:

考虑设 dp(i,j,k) 表示当前枚举到了第 i 个数,最大公约数为 j,选了 k 个数的方案数。方程为 dp(i,gcd(ai,j),k)=dp(i,gcd(ai,j),k)+dp(i1,j,k1)

100 pts:

考虑容斥。互质数量 = 总数 - 不互质数量。设 numi 表示数列中有多少个 i 的倍数。理论上讲,答案是 Cn4Cnumi4。但这样会有重复,例如 num2num4。同时如果 i 含有多个相同质因子,例如 num8,也会造成重复。因此去除他们后,答案应该是:

Cn4Cnum24Cnum34+Cnum2×34+Cnum2×54+=Cn4+Cnumi4×(1)cnt

其中 cnt 表示 i 所含不同质数的数量。

这个式子求解的方式很多,暴力、线筛等等都可以。

复杂度 O(nai)

3 挂分

  • T1 中不小心将 num 写成了 cnt10050​。

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(i,j,0)=max(dp(i1,j+1,0),dp(i1,j1,1))dp(i,j,1)=max(dp(i1,1,0),dp(i1,j1,1))+Didp(i,0,0)=max(dp(i,0,0),dp(i1,0,0))

暴力转移即可。初始状态 dp(0,0,0)=0,结果为 dp(n+1,0,0)

Solution 2:

dp(i,j) 表示第 i 天疲劳值为 j 能跑的最长距离。

则有状态转移方程:

{dp(i,j)=max(dp(i1,j1)+Di)dp(i,0)=max(dp(i,0),dp(i1,0))dp(i+j,0)=max(dp(i+j,0),dp(i,j))

暴力转移即可。

两者复杂度均为 O(nm)

T3

10 pts:

考虑暴力 DFS。直接枚举每一个位置放还是不放。

可以优化该算法,这样甚至能过掉样例:我们改为枚举每一列放多少个点,利用组合数计算即可。

100 pts:

首先要发现一个性质:对于在模 n 意义下相同的列 i,当中放的点的个数都是相等的。这个容易证明。

也就是说对于点的个数来讲,他们每 n 个是一个循环节。

因此我们不必枚举所有的 m,只需要枚举 n 即可。

考虑 dp。设 dp(i,j) 表示枚举到第 i 列,当前放了 j 个的方案数。那么有方程:

dp(i,j)=dp(i1,jp)×(Cnp)mn+[immodn]

那么这样的复杂度是 O(n4logm),无法通过。

我们可以令 num(i,p)=(Cnp)mn+[immodn],这样 O(n2) 预处理出 num(i,p),复杂度就是 O(n4)。可以通过。

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

首先我们可以推出一个式子:对于 abcd,一定有 ab+cdac+bdad+bc

考虑将其推广到更高元,仍然成立。

根据这个结论,我们就可以直接来做这道题。将数列排序,前一半用大乘小,后一半相邻的数相乘。最后两者作差即为答案。

T2

看到 最小覆盖最大,直接可以想到二分答案。

二分最后的 size,然后考虑怎么检查。贪心是不行的,考虑 dp。

dp(i) 表示选到第 i 个位置上能得到的最大覆盖长度。直接由题意得状态转移方程为:

dp(i)=maxiwijisize{dp(j)+ij,dp(i1)}

注意后一种是不可选的情况。

我们发现 j 在一段连续区间,因此可以单调队列优化 dp。

T3

首先考虑一个经典典中典的树上技巧:up and down。

具体的,设 f(i,0/1) 表示 i 的子树内选点的方案数,g(i,0/1) 表示子树外选点的方案数。

首先看 f(i,0/1),方程很简单:

f(i,0)=max{f(soni,0)+f(soni,1)}f(i,1)=max{f(soni,0)}

接下来看 g(i,0/1),其实也很简单:

g(i,0)=g(fa,0)×f(fa,0)f(i,0)+f(i,1)+g(fa,1)×f(fa,1)f(i,0)g(i,1)=g(fa,0)×f(fa,0)f(i,0)+f(i,1)

最后我们来看答案的式子。对于一条边,我们只需要统计两个点选或不选的总方案数即可。至于怎么求,利用 f(i,0/1),g(i,0/1) 即可,如下:

ansu,v=f(v,1)×f(u,1)f(v,0)×g(u,1)+f(v,0)×f(u,1)f(v,0)×g(u,1)+f(v,1)×f(u,0)f(v,0)+f(v,1)×g(u,0)

这是我们发现,公式中有除法,结果还要取余,因此只能用逆元求解。

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 次为一个周期,因此我们每一行只需要维护 Cmod4 的情况即可。

T2

30 pts:

容易发现这是二分图匹配,暴力计算即可。

100 pts:

首先容易证明,对于一个男生要选个子比他高的,选尽可能低的那一个对于后面的男生更有利。

因此我们考虑贪心,将两种人分开考虑,一个升序一个降序排列,利用贪心求解即可。

T3

首先我们发现,最小生成树的边权之和是 n1,因为 1 和所有点都有连边。

因此所有最小生成树的边权和都是 n1,也就是说,最小生成树上的父子一定互质。

又因为父亲的编号小于儿子的编号,因此一个点的父亲的方案数就是小于他且与他互质的数的个数。显然这就是欧拉函数的定义。

因此最后答案就是 i=1nφ(i)

T4

折半搜索的模板题。

考虑爆搜的复杂度是 O(2n),过不了 n=40

我们考虑将区间分成两段,这样复杂度就是 O(2n2),能够通过。

我们记录下两段区间分别可以凑出的数 pi,qi,然后我们要找出两个序列中的一对数,使得他们的和接近 m

显然直接枚举 pi,然后在 q 中二分查找即可。

3 挂分

  • T1 移动骰子时的转移写错,10070​。
  • T3 忘记了 φ 函数,导致离正解只有一步之遥。

6 2024.3.10 测试

1 得分

题目 T1 T2 T3 T4 总分
得分 80 60 30 45 215

排名:rank 7

2 题解

T1

此题就是一个模意义下的背包,设 dp(i,j) 表示选了 i 个数,总和余数为 j 的方案数。那么由方程:

dp(i,j)=dp(i1,j)+dp(i1,(jai)modk)

暴力计算即可。

T2

又是典中典的 up and down。

第一遍 DFS 可以直接维护子树内最长链,然后在第二个 DFS 中求出子树外最长链。但是这样会有一个问题,如果在第二个 DFS 时,这个节点就是父节点的最长链上的节点,那么就不能简单的用最长链更新。解决方法也很简单再记录一个次长链即可。

记子树内最长链为 fi,子树内次长链为 gi,子树外最长链为 pi。因此,当该节点是父节点最长链上的点时,pi=max(gfa,gfa)+1,否则 pi=max(ffa,gfa)+1

T3

首先列出这样一个显然的式子:C(i)=C(i1)10p+i。其中 pi 的位数。

显然 O(n) 递推无法满足,我们考虑优化。这种齐次线性递推显然可以矩阵快速幂优化。但是我们要将 i 拆成 i1+1。最后的矩阵是:

[C(i)i1]=[10p11011001][C(i1)i11]

其中,我们可以直接枚举 p 进行计算。

T4

首先,k=8,显然考虑状压。考虑设 dp(i,j,s) 表示枚举到 i 个点,共连了 j 条边,从 iik 的点的度的奇偶状态(0 为偶,1 为奇)。

但是我们会发现这样不好实现,因此在加入一维。设 dp(i,j,s,l) 表示枚举到 i 个点,共连了 j 条边,从 iik 的点的度的奇偶状态,当前只考虑 iik+l 的方案数。

接下来我们分析。

如果现在不连 iik+l 这条边,那么 dp(i,j,s,l+1)=dp(i,j,s,l)​。

如果现在要连 iik+l 这条边,那么 dp(i,j+1,s2k2l,l+1)=dp(i,j,s,l)

最后我们考虑转移到下一个点,只有当 s 的最后一位,即 ik 的度时偶数时才可以转移,因为从此以后再也不会更新 ik 了。

此时方程为 dp(i+1,j,s >> 1,0)=dp(i,j,s,k)

初始化是 dp(2,0,0,0)=1,答案是 dp(n+1,m,0,0)

3 挂分

  • T1 写了一个非常傻逼的 O(n2k) 复杂度,还没发现。
  • 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=i=17ai,接下来推概率公式,对于选 7 个数按顺序构成 17 的概率是:

a1N×a2N1×a3N2×a4N3×a5N4×a6N5×a7N6

然而帕琪七重奏只需要构成 17 排列即可,因此方案数为:

7!×a1N×a2N1×a3N2×a4N3×a5N4×a6N5×a7N6

现在计算在整个长为 N 的数列中出现一次帕琪七重奏的概率。由于一个数列有 N6 个位置可以放下帕琪七重奏,所以将上面概率乘上 N6 即可。最终答案为:

7!×a1N×a2N1×a3N2×a4N3×a5N4×a6N5×a7

T3

本题按照部分分做基本可以拿到满分。

最开始的一步:发现 n<m,因此我们尽可能枚举 n。考虑到可以将 [1,n] 值域内的答案全部计算出来,再对于 m 查询即可。

O(n4) 40 pts:

直接暴力枚举 Xa,Xb,Xc,Xd 即可。

O(n4) 55 pts:

发现由于 Xa<Xb<Xc<Xd,因此每次都可以在上一个数加一的地方开始枚举。

O(n3) 85 pts:

从这里开始就是提示正解了。

首先,如果确定 XdXc=t,那么显然 XbXa=2t

由于有 XbXa<13(XcXb),就可以得到 2t<13(XcXb),即 XcXb>6t

我们再记 XcXb=6t+k(k>0)​。

此时枚举 Xa,t,k,就可以计算出 Xb,Xc,Xd

我们记 t(x) 为一个桶,那么对于这四个数字,每个数字对应的答案都应该乘上其他三个数字的 t 值。例如 ansXa=t(Xb)×t(Xc)×t(XD)

O(n2) 100 pts:

考虑对上面的算法再进行一些优化。

如果我们考虑只枚举 D,t,是否可行呢?

此时 C 好求,为 Dt。然而 k 未知,似乎求不出 A,B

此时我们发现,令 k=1 即可得到 A,B 的最大值。同时我们还发现,对于小于这个最大值 A,B 的数 A,B 也是满足要求的!

因此我们可以在正序枚举 D 的时候记录 t(A)×t(B) 的前缀和,这样就可以求出 ancC 以及 ansD

然后再倒序枚举 A,记录 t(A)×t(B) 的后缀和,就能求出 ansA 以及 ansB 了。

T4

首先看到这个题目的描述自然会想到区间 dp。

然后我们又发现 k8,并且还只有 01 串,因此还要进行状压 dp。

考虑如何设计状态。设 dp(i,j,S) 为将区间 [i,j] 最终合并为状态 S 的最大得分。

我们枚举断点 mid,此时我们希望右边的区间可以 合并为一个字符,那么 mid 就只能等于 j,jk+1,j2k+1。此时会有状态转移方程:

dp(i,j,S×2)=max{dp(i,mid1,S)+dp(mid,j,0)}dp(i,j,S×2+1)=max{dp(i,mid1,S)+dp(mid,j,1)}

其中 ×2 就是左移一位。

但是还有一种特殊的情况,即如果整个区间本身就能合并为一个字符的情况。那么我们又会有转移方程:

dp(i,j,c(S))=max{dp(i,j,S)+w(S)}

但是又有一个问题,当 c(S)=S​ 的时候 dp 数组会自我更新,因此还要用一个辅助数组计算后再赋值即可。

3 挂分

今日无挂分。

8 2024.4.5 测试

1 得分

题目 T1 T2 T3 T4 总分
得分 28 0 0 100 128

排名:rank 5

2 题解

T1

首先,根据约数和定理,一个数的正约数之和为 i=1j=0aipij。而要求这个正约数之和为 S

显然枚举是不好实现的,但是我们还有一个强有力的工具——爆搜。

我们在一层内枚举 j=0aipij,同时累乘 piai。将 S 除掉 j=0aipij​ 后进入下一层继续搜索。

然后剩下的就是细节:

  • 我们无法枚举 109 之内所有质数,枚举到 105 左右即可。剩下的质数需要单独特判。
  • 注意循环的时间复杂度,需要优化。

T2

一道比较简单的概率。

首先我们肯定是设 fi 表示取到 i 张邮票后还要付的期望钱数。然而你会发现这推不出什么。

问题在于这道题的钱数和次数是相关的,而且次数也不容易求出。

因此我们再设 gi 表示取到 i 张邮票后还要取的期望次数。那么这个还算好求,分类讨论即可:

  • 当这一张以前抽到过,则 gi=in(gi+1)
  • 当这一张以前没有抽到,则 gi=nin(gi+1+1)

二者相加即可。但是此时右边还有 gi,于是移项后得:

gi=nni+gi+1

那么有了 g 数组,f 数组就好求一些了。仍然分类讨论:

  • 当这一张之前抽到过,则 fi=in(fi+gi+1)
  • 当这一张以前没有抽到,则 fi=nin(fi+1+gi+1+1)

这个式子看起来有点鬼畜,解释一下:以第二个为例,fi+1 是显然的,而此时又拿了一个,于是第 i+1 次往后的所有取了的邮票的钱数都要加一(相当于他们被取的次数增加了),然后再加上第 i 个这一个 1,就得到了这个式子。

此时仍然需要移项,后得:

fi=ini(gi+1)+gi+1+fi+1+1

暴力计算两个数组即可。

T3

个人认为最难的一道题。

首先关注构成的树的总个数,显然是 n!​ 个。于是原题其实就是求最后的路径之和。

接着我们考虑如何计算路径之和。这里采用非常典的拆贡献,对于每条边,走过它的次数就是 2sizi(nsizi)。但是这样加起来每条边都多算了一次,因此一条边的走过次数就是 sizi(nsizi)

接下来考虑如何求总的结果。此时我们并不需要具体求出 siz,这需要知道每一种 siz 对应的方案数即可。

下面先看一段证明:

考虑这样一件事:对于一个点,其子树大小为 k​,那么还会有多少个剩下的位置可以放?

引理:每多放一个点,就会多一个位置。

证明:显然。对于放下的那个位置,会使得位置减一。但同时这个点的两个儿子又使得位置加二。因此位置加一。

于是对于上面的问题,答案就显然为 k+1

同时如果子树外有 k 个点,那么位置就是 k+11=k

此时我们设 dp(i,j,k) 表示当前子树根节点为 i,枚举到 j 个点,i 的子树大小为 k 时的方案数。分类转移:

  • j 号点没有放在 i 的子树内,则此时剩下的位置数量就是 j1k,于是方案数为 dp(i,j1,k)×(j1k)
  • j 号点放在 i 的子树内,则此时剩下的位置就是 k,于是方案数为 dp(i,j1,k1)×k

综合起来得:

dp(i,j,k)=dp(i,j1,k)×(j1k)+dp(i,j1,k1)×k

此时我们发现一个严重的问题,这样的复杂度是 O(n3)。不过又可以发现,转移方程与 i 无关,因此将 i 合并,只保留 dp(j,k) 即可。

接下来考虑初值,显然有 dp(1,k)=k!

最后我们将方案数乘上边的次数即可,答案就是 i=1ndp(n,i)×i×(ni)

T4

正常想法:

首先考虑转化为每条边期望经过的次数,对于一段区间,显然是 i=lr(il+1)(ri+1),于是总的期望就是 i=lrvi(il+1)(ri+1)

将其拆开得 i=lrivi×(r+1)i2vivi×(l1)(r+1)+ivi×(l1)

由于题目中提到了要区间修改,区间查询,那么我们首先想到线段树。

再考虑上面的式子,我们只需要维护 vi,ivi,i2vi

但是这样我们前面的系数不能随着区间来变化,因此我们实际处理中还要进行一些加减。

非正常想法:

首先,打表出每一个区间长度下每一个收费站期望经过的次数:

1

2,2

3,4,3

4,6,6,4

5,8,9,8,5

不难发现第 i 列都是顺次 i 的倍数。

因此我们可以写出通用的式子:i=lrkal+2(k1)al+1+3(k2)al+2++(rl+1)(kr+l)ar

对于这个式子,展开处理,后面就与正常做法相同了。

3 挂分

今日无挂分。

9 2024.5.2 测试

1 得分

题目 T1 T2 T3 T4 总分
得分 100 36 100 10 246

排名:rank 2

2 题解

T1

大模拟题。

直接按照题意维护即可。利用栈判断循环的开始和结束,同时统计复杂度。注意 x>y 时不计算内部的复杂度。

T2

分层赋分题。

将题目看做在矩阵中选点,则每一行只能有一个点。

36pts: 暴力枚举每一行的点在第几列(或不选),判断即可。复杂度 O(mn)

64pts:

考虑到 m 较小,因此可以作为 dp 状态。设 dp(i,j,k,l) 表示前 i 行中第 1 列放了 j 个,第 2 列放了 k 个,第 3 列放了 l 个的方案数。

那么显然会有状态转移方程 :

dp(i,j,k,l)=dp(i1,j,k,l)+dp(i1,j1,k,l)×a(i,1)+dp(i1,j,k1,l)×a(i,2)+dp(i1,j,k,l1)×a(i,3)

最后答案为 max(j,k,l)j+k+l2j+k+l0dp(n,j,k,l),复杂度 O(nm+1)

到此就是暴力方法能够拿到的最好分数了。下面开始正解。

84 pts:

考虑到如果不合法,那么只可能有一列超出总数的一半。

而这样就意味着不合法情况是好求的,那么可以考虑容斥,用总方案数 - 不合法方案数。

先看总方案数。每一行可以任意选一个或者不选,令 Si=j=1ma(i,j),那么总方案数就是 i=1m(Si+1)。但是我们不能选空集,因此还要减一。

接下来是不合法方案数。由于不合法的列只可能有一个,我们就先枚举它。设当前枚举到的列为 c

仍然考虑 dp,设 dp(i,j,k) 表示前 i 行中,第 c 列总共放了 j 个,其他列总共放了 k 个的方案数,那么就有转移方程:

dp(i,j,k)=dp(i1,j,k)+dp(i1,j1,k)×a(i,c)+dp(i1,j,k1)×(Sia(i,c))

最后答案为 j>kdp(n,j,k)。复杂度是 O(mn3) 的,仍然不够优秀。

100pts:

考虑到上面的步骤中哪里最浪费时间,显然是 dp。我们发现我们的最终答案对于 j,k 的具体数值并不关心,只关心 j>k 这个条件。

那么我们便可以将后两维合并成 jk,枚举 jk 即可。这样我们就会得到最后的转移方程(dp(i,p) 表示前 ijk=p 的方案数):

dp(i,p)=dp(i1,p)+dp(i1,p1)×a(i,c)+dp(i1,p+1)×(Sia(i,c))

最后答案为 p>0dp(n,p),复杂度 O(mn2),可以通过。

T3

首先发现 n14,因此立刻想到状压。

我们设 dp(i,S) 表示深度为 i 时总共建立的地铁状态 S 时的最小花费,那么显然有方程:

dp(i,S)=minTS{dp(i1,ST)+cost(i,T)}

这里 ST 表示从 S 集合中去掉 Tcost(i,T) 表示在深度为 i 的地方修建地铁方案为 T 的花费。

那么显然我们现在要解决两个问题:costdp 的求解。

先看 cost,我们想通过预处理求解。

不妨再设一个数组 sub(i,j),表示地铁 i 修建在深度为 j 的地方的花费。显然求解 sub 数组是 O(n2m) 的。

接下来考虑 cost,我们不能完全利用 sub(i,j) 求解,因为还需要判断合法。此时再再设一个数组 flg(i,j),表示第 i 条地铁能否与第 j 条地铁在同一深度修。显然这个数组也是可以 O(n2m) 求解的。

那么 cost 此时就可以求出来了,判断合法并求和,复杂度是 O(n32n)

接下来考虑求解 dp 数组,按照朴素的算法我们需要枚举两次子集,这样算是 O(n2n2n)=O(n4n) 的,十分不优秀。

我们考虑下面这种奇妙的技巧:

for (int T = S; T; T = (T - 1) & S)

感性证明:

我们其实可以直接观察 T 的变化,立马就能理解。设 S=(10110)2,那么 T 的二进制变化就如下:

1011010100100101000000110001000001000000

可以观察到枚举是没有浪费且不重不漏的。

复杂度说明:

首先我们在该循环的外层枚举了 S,而内层本质上是选出含有 1 的子集。设总共有 k1,则内层的复杂度就是 O(2k)

考虑到外层实际上就是在 n 个数中随便选 1,因此总的复杂度我们可以写成 k=0nCnk×2k。那么我们改写成 k=0nCnk×2k×1nk。根据二项式定理可得这就是 (1+2)n,即 3n

因此这一部分的复杂度其实是 O(3n)

那么通过上面的分析,我们就通过这样的优化将复杂度优化至 O(n3n),可以压线通过了。

最后复杂度是 O(2n2m+n32n+n3n),不是很能跑满所以压线能过。

T4

我们可以想到用子树来更新当前的值。假如当前节点是 u,有一个儿子 vvalv 的值是 (vc1+d(c1,x))(vc2+d(c2,x))(vck+d(ck,x)),那么由于 uv 的父亲,所以此时所有的 d 都会加一。因此 valu 的一部分应该是 (vc1+d(c1,x)+1)(vc2+d(c2,x)+1)(vck+d(ck,x)+1)

我们将 u 的所有儿子 v 的信息合并到自己,给每一项加一求出异或和,最后再异或上自己的 vu 就可以得到 valu 了。

考虑到这里面有位运算操作,还要进行一系列合并维护,想到利用 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 变为 11 变为 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 下的时候再重新计算即可。

形式化的讲,我们给每一个要送出的礼物在栈中的下标记为 pi。那么对于一个 i,我们找到它右边第一个满足 pj>pij,则 (i,j) 区间内的礼物都是在 i 之上的,经过排列每一次取出礼物都只需要 1 单位时间。

这样我们只是对于每一个礼物进行遍历,复杂度 O(m)

T2

首先考虑暴力。修改操作是 O(1) 的,关键在于查询。

我们令每一位上最后可能得取值为 pi,则答案就是 pi

关注 pi 的求法。如果这一位上 01 同时存在,显然不满足,pi=0;如果这一位上只出现 01 以及若干个 2(可以不出现),那么 pi=1;如果全部是 2,那么 pi=2

这样做是 O(qnm) 的,不够优秀。

我们首先发现这样一件事:在求出 pi 的时候,我们其实只需要求出这一位上有多少个 01 即可求出答案。那么现在我们把整个字符的矩阵转过来看,就是在一个区间上修改一位,同时求出区间上 01 个数的和。这显然可以使用线段树维护。

那么总共有 n 位,正好 n 只有 30,因此是可以开的下的。复杂度 O(qnlogm)

但是我们发现这样做运算量达到了 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,我们在上一次打的时候就要打 jk 次并且还要剩一发子弹。显然直接是 dp(i1,jk,1)。此时在加上打 k 次的分数就可以得到:

dp(i,j,1)=max{dp(i1,jk,1)+v(i,k,1)}

接下来是 dp(i,j,0)

首先我们显然会有 dp(i,j,0)=max{dp(i1,jk,0)+v(i,k,0)}。但是根据我们上面借子弹的理论,我们是可以多打几个 Y 砖块的。接下来分类讨论一下借子弹的对象:

  • i 给前 i1 列借

此时我们最后一发打到的是 i 上的 N 砖块。假设我们当前要打的子弹数是 k,我们在上一次打的时候就要打 jk 次。我们在打完前 k1 发之后,将剩下的这一发子弹借给前面的列,这样前面的列就可以打 Y 砖块。在打完 Y 砖块后会剩下一发子弹,再还给第 i 列就可以打完了。

于是我们的转移方程就是:

dp(i,j,0)=max{dp(i1,jk,1)+v(i,k,0)}

  • 当前 i1 列给第 i 列借。

同理,这样最后一发打到的是前 i1 列。我们在打完前 k1 发后把子弹借给 i 列,让 i 列多打几个 Y 砖块,打完后把子弹还回去即可。转移方程为:

dp(i,j,0)=max{dp(i1,jk,0)+v(i,k,1)}

然后我们直接转移即可。最后答案为 dp(m,k,0),因为如果最后一发打到的是 Y,那么就一定会多出一发子弹,肯定不优。

时间复杂度 O(nmk)

T4

爆搜即可。

具体来讲,我们只需要判断顺子和带牌,剩下的我们都可以看成散牌,然后直接统计即可。

3 挂分

  • T2 动态开点线段树空间开小,6051
posted @   UKE_Automation  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
点击右上角即可分享
微信分享提示