常见错误之除零错误。
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=17130

一般来说,只要除数不是一个简单的,一个变量,都要担心这个除 0 错误。
1 | void gen_eraly_seg() { |
常见错误之除零错误。
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=17130

一般来说,只要除数不是一个简单的,一个变量,都要担心这个除 0 错误。
1 | void gen_eraly_seg() { |
Lucas 定理的最直观证明是通过生成函数(母函数)和二项式定理来推导的。
首先,根据二项式定理,我们知道组合数 Cnm 其实就是多项式 (1+x)n 展开后 xm 这一项的系数。
对于任意质数 p,考虑 (1+x)p(modp) 的展开式:
(1+x)p=Cp0x0+Cp1x1+Cp2x2+⋯+Cppxp
因为 p 是质数,当 1≤i≤p−1 时,Cpi=i!(p−i)!p! 的分子里有一个 p,而分母里没有 p 可以把它约掉。所以中间所有的项都是 p 的倍数,在模 p 意义下全部等于 0。
因此我们得到了一个非常漂亮的核心结论:
(1+x)p≡1+xp(modp)
现在我们来求 (1+x)n(modp) 中 xm 的系数。
我们将 n 和 m 拆分成除以 p 的商和余数(类似于把它们转成 p 进制):
令 n=⌊n/p⌋×p+(nmodp)
令 m=⌊m/p⌋×p+(mmodp)
代入多项式中:
由于前面证明的核心结论,把内部的 (1+x)p 替换掉:
(1+x)n=(1+x)⌊n/p⌋×p+(nmodp)=((1+x)p)⌊n/p⌋×(1+x)nmodp
(1+x)n≡(1+xp)⌊n/p⌋×(1+x)nmodp(modp)
我们要求的是 xm 的系数,而 xm=(xp)⌊m/p⌋×xmmodp。
所以在右边这两个括号相乘时,我们必须:
从前一个括号 (1+xp)⌊n/p⌋ 中选出 (xp)⌊m/p⌋,这部分的系数是 C⌊n/p⌋⌊m/p⌋。
从后一个括号 (1+x)nmodp 中选出 xmmodp,这部分的系数是 Cnmodpmmodp。
两者相乘,就得到了 xm 的总系数。这就是 Lucas 定理的来源:
Cnm≡C⌊n/p⌋⌊m/p⌋×Cnmodpmmodp(modp)
注意观察这道题目的输入格式:每一组询问都会给出一个不同的质数 p!
我们刚才在你的 main.tex 里,把 Lucas 定理改成了复用你已有的 Comb 命名空间和全局的 mod。那个模板只适用于所有询问的模数 p 固定不变的情况(因为我们需要针对固定的 mod 预处理阶乘)。
对于这道题,因为 p 会变(最高到 105),所以我们不能用全局预处理,反而应该用我们刚才撤掉的那份按定义暴力算的代码!因为暴力算单次是 O(p) 的,加上 Lucas 递归,总时间大概是 O(plogpn),这道题 n≤20 组询问,最大运算量在 2×106 左右,完全可以光速通过。
以下是可以直接通过这道题的 AC 代码,风格和我们刚才讨论的完全一致:
AC
https://www.acwing.com/problem/content/submission/code_detail/44735227/
1 | #include <iostream> |
题目描述
需要构造 n 个具有 k 个面的骰子,每个面上的数字取值范围为 0∼m。
两个骰子进行对决时,随机掷出一个面,点数大者获胜。要求构造的骰子组合必须同时满足以下三个条件:
循环劣势:形成 1→2→3→⋯→n−1→n→1 的胜负关系。即 1 号骰子输给 2 号,2 号输给 3 号,……,n 号输给 1 号(“输给”定义为在 k×k 种可能的掷出组合中,获胜的次数严格少于对方)。
绝对无平局:每个数字至多只能出现在一个骰子上,保证任意两个骰子对决时不可能掷出相同的点数。
字典序最小:将 1 号到 n 号骰子上的所有数字按顺序拼接成一个长度为 n×k 的序列,要求该序列在所有合法方案中字典序最小。
输入格式
第一行输入一个正整数 T,表示测试数据组数。
接下来每组数据输入三个正整数 n、m、k,分别表示骰子数量、面上的最大允许数字、每个骰子的面数。
输出格式
对于每组数据,输出一行 n×k 个用空格分隔的整数,依次表示 1 号到 n 号骰子各个面上的数字。
样例数据
输入样例:
1 | 1 |
输出样例:
1 | 0 3 3 1 1 4 2 2 2 |
样例解释
输入代表需要构造 3 个骰子,每个骰子有 3 个面,数字范围在 0∼4 之间。根据输出样例,三个骰子的构造如下:
1 号骰子:[0, 3, 3]
2 号骰子:[1, 1, 4]
3 号骰子:[2, 2, 2]
胜负关系验证(总对决情况数为 3×3=9 种):
1 号 vs 2 号:1 号获胜的情况为 3>1(共 4 次),2 号获胜的情况为 1>0、1>0、4>0、4>3、4>3(共 5 次)。2 号胜率更高,1 号输给 2 号。
2 号 vs 3 号:2 号获胜的情况为 4>2(共 3 次),3 号获胜的情况为 2>1(共 6 次)。3 号胜率更高,2 号输给 3 号。
3 号 vs 1 号:3 号获胜的情况为 2>0(共 3 次),1 号获胜的情况为 3>2(共 6 次)。1 号胜率更高,3 号输给 1 号。
该方案数字各不相同(无平局),构成了 1→2→3→1 的循环劣势,且可以证明拼接后的序列 0 3 3 1 1 4 2 2 2 是所有满足条件的方案中字典序最小的。
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=17028
这道题是一道非常精妙的构造题,涉及博弈与贪心策略。为了得到字典序最小的方案,我们需要深入分析骰子胜负的数学本质。
我们需要构造 n 个具有 k 个面的骰子,每面的数字范围为 0∼m。要求形成 1→2→3→⋯→n→1 的循环劣势(即 i+1 击败 i,1 击败 n),其中“击败”定义为在 k2 次可能对决中获胜次数严格大于一半。不仅要满足该条件,还要使所有骰子的面按顺序拼接后的序列字典序最小。
1. 胜负条件的数学转化:
由于不能平局,任意两个骰子对决共有 k2 种结果。“B 击败 A” 等价于 B 在对决中获胜的次数 W(B,A)≥⌊k2/2⌋+1。
令目标获胜次数为 w=⌊k2/2⌋+1。
2. 字典序最小的贪心框架:
为了让整体拼接成的序列字典序最小,我们需要让 1 号骰子的数字尽可能小,且小数字尽可能多;接着是 2 号骰子,依此类推。
这启发我们设计一种 “两阶段划分(双段构造)” 的模式。将每个骰子 i 的 k 个面分成两部分:
1 | #include <iostream> |
x、early_val 和 late_val 这些一维状态数组,占用空间为 O(n)。这远低于题目宽松的 512 MB 空间限制。1 | \documentclass{article} |

首先,我们为什么会想到双段构造?呃,其实正常的思维链路应该是,我们先想怎么构造,
其实题面里面讲的非常清楚,一个骰子最多只会有两种不同的数字,每个数字仅能出现在至多一个骰子上。

那都说了,最多两个数,那还能不是双段构造吗?
如果分成双段构造,那么不难看出:
早段1<早段2<⋯<早段n,$\text{晚段}_1 < \text{晚段}_2 < \dots < \text{晚段}_n $(为了字典序最小,这两个条件是必须的),但是早晚段之间是什么关系呢?
注意啊,为了赢得游戏,早段n<晚段1,这个也是为了赢得游戏,满足所谓的“循环劣势要求”,所必须的这个构造,一个的最小的,大于一个的最大的,两者之间是绝对不会发生这个穿插的这个情况的,因此就是 早段1<早段2<⋯<早段n<晚段1<晚段2<⋯<晚段n。
okay,那这道题目比较关键的部分就做完了,当然,还需要处理一些细节问题,就是每个段的早段是几个数,晚段是几个数?
不过贪心求解的部分也不是那么容易的。
首先,先写出一系列的这个获胜条件:

接着,我们要进行这个贪心求解,我们不难发现:

然后我们知道了 x1 的值,后面就用前面的限制,一个一个往后推就行了。
1 | void gen_eraly_seg() { |
AC
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=17137
1 | // teamname: Gospel_rock |
除零错误——INTEGER_DIVIDE_BY_ZERO
题目描述
你有三个栈 A,B,C 和两个变量 L,R(相当于两只手)。栈中元素必须时刻满足:越靠近栈底的元素越大。
初始时,L=R=0,A 栈中有 n 个元素(从栈底到栈顶依次为 n,n−1,…,1),B,C 栈为空。目标是将所有元素移动到栈 C,且操作过程中必须满足上述大小限制。
可以进行两种操作:
操作 1(拿起):选择一个非空栈 S 和一个值为 0 的变量 V(L 或 R),弹出 S 的栈顶元素,并将该元素的值赋给 V。
操作 2(放下):选择一个值为 x=0 的变量 V 和一个栈 S,将 x 压入栈 S,并将 V 的值设为 0。
求:将所有元素移到栈 C 所需的最少操作 1(拿起)的次数。
结果需要对 998244353 取模。
输入格式
第一行输入一个整数 T(1≤T≤100)表示测试数据组数。
接下来 T 行,每行输入一个整数 n(1≤n≤106)。
输出格式
输出 T 行,每行一个整数,表示最少使用操作 1 的次数对 998244353 取模的结果。
样例输入
1 | 5 |
样例输出
1 | 1 |
样例解释(对于 n=3)
初始状态: A(3,2,1),B(),C(),L=0,R=0
操作 1: A(3,2),B(),C(),L=1,R=0
操作 2: A(3,2),B(1),C(),L=0,R=0
操作 1: A(3),B(1),C(),L=2,R=0
操作 1: A(),B(1),C(),L=2,R=3
操作 2: A(),B(1),C(3),L=2,R=0
操作 2: A(),B(1),C(3,2),L=0,R=0
操作 1: A(),B(),C(3,2),L=1,R=0
操作 2: A(),B(),C(3,2,1),L=0,R=0
共使用了 4 次操作 1,因此答案为 4。
1 | \documentclass{article} |
直接打表是没什么思路的:
1 | 1:1 |
再大,时间复杂度就要求太大了,没法继续打了。我们一般看规律是用差分与二阶差分这样子去看的,不过这个难度还是比较大,想把这个规律看出来,主要是
不过,汉诺塔问题嘛,就是 3 步:

当然,由于我们现在有两只手,所以情况稍微复杂一点,就是我们在没有辅助杆的情况下,我们可以移动的盘子就不一定是 1 个了,

当然,具体是几根也不是那么重要了。可以使用 bfs 求解,或者你对自己的直觉和数学功底比较自信的话,也可以自己整一整。
1 | 没有辅助柱子的情况下,就靠两只手,所需要的拿去次数 |
其实套路和经典汉诺塔问题一样。

AC
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=18423
1 | // teamname: Gospel_rock |
打表的时候遇到的问题
题目大意
定义一种自然数下的二元运算「月球异或」(记作 ⊛):将两个自然数转换为三进制后,对每一位分别相加并对 3 取模(即三进制下的不进位加法)。例如 8(10)⊛16(10)=022(3)⊛121(3)=110(3)=12(10)。
现给定一个长度为 N 的自然数序列 v1,v2,…,vN。你可以对序列中的任意元素 vi 执行以下两种操作任意次(包含零次):
vi←vi⊛vi
vi←0
有 Q 次独立的询问,每次询问给出一个自然数 s。对于每次询问,你需要判断能否通过若干次上述操作,使得序列全部元素的「月球异或」和等于 s(即 v1⊛v2⊛⋯⊛vN=s)。
数据范围
测试组数:1≤T≤100000
序列长度与询问次数:1≤N,Q≤106,且所有测试组的 ∑N,∑Q≤2×106
序列元素与询问值:0≤vi,si≤1018
样例数据
输入:
1 | 3 |
输出:
1 | Yes |
样例解释
以第一组样例为例:
此时 N=1,Q=4,序列只包含一个元素 v1=1。
四次询问的值分别为 s=0,1,2,3。
对于 v1=1(三进制表示为 01(3)),它可能变化得到的状态有:
执行零次操作:得到原来的值 1。
执行一次第一种操作:1⊛1=1(3)+1(3)(mod3)=2(3)=2。
执行一次第二种操作:直接变为 0。
因为操作本质上是对三进制的每一位独立进行乘法并对 3 取模,或者直接置零,所以原本为 0 的高位(如 31 对应的位)永远不可能变成非 0 值。
目标 s=3 在三进制下表示为 10(3),其第 1 位(对应数值 3)的值为 1,而 v1 无论怎么操作在第 1 位上永远是 0,因此永远无法得到 3。
最终对于 0,1,2,3 的询问,前三个可以得到,后一个无法得到,分别输出 Yes、Yes、Yes、No。
其实,不难发现,我们其实就是要看一个数字能不能用我们的这个三进制异或线性基表示出来。
1 |
|
AC
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=7820&from=rank
1 | //teamname:Gospel_rock |