专栏文章
题解:P7386 「EZEC-6」0-1 Trie
P7386题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @miocjbd5
- 此快照首次捕获于
- 2025/12/02 16:58 3 个月前
- 此快照最后确认于
- 2025/12/02 16:58 3 个月前
前言
若无特别说明,文中所有数字均为正整数。
题意
给定 个
1 和 个 0,要求用它们组成一个 的串,并满足:- 开头是
0,结尾是1。 - 两个
1不能相邻。
解法
1 不能相邻这个要求很难受。所以我们不妨把 1 和 0 合并起来,这样就不用考虑相邻的问题了。题面有个要求:开头必须是
0,最后必须是 1,所以我们考虑将 10 合并。为何不考虑 01 合并?
如果我们使用了
01 合并,那么串如果形如 01... 将不方便处理,而且无法处理与最后的 1 相邻的情况。
简单地说就是 10 合并更方便。我们把开头的
计算串的数量我们需要先知道有多少
0 和最后的 1 独立出来考虑:开头的 0 显然只占了一个结点,所以对答案的贡献仅有 ,而计算最后的 1 用了多少结点实际上就是在计算能生成多少种串。计算串的数量我们需要先知道有多少
10 和单独的 0(下文将简称为 0)。因为去掉了开头结尾,所以还剩下 个 1 和 个 0,因为我们合并了 10,所以 10 的个数为 个,此时还会被拿去 个额外的 0,所以实际上 0 的数量有 个。一个小细节
根据上面的结果,应有 ,即 ,否则无解。
那么串的种类数就显然是 了。我们将
所以结尾
接下来我们就要考虑中间产生了多少贡献。
我们先考虑用 dp 的方式解决。
设 为中间段有 个
这里一般递推的情况中,对两边进行了分类讨论。若此时使用
一般地,这种递推都可以写成组合数的和。现在我以这个题为例讲解。
对于任意的 ,我们把结果分为三个部分:
10 和 0 都看成是 个元素就容易推得上式。所以结尾
1 的数量是 。接下来我们就要考虑中间产生了多少贡献。
我们先考虑用 dp 的方式解决。
设 为中间段有 个
0, 个 10 时所产生的贡献,此时我们令 Trie 上存在一个父亲结点容纳下面的点。我们容易得出以下递推:这里一般递推的情况中,对两边进行了分类讨论。若此时使用
0,那么贡献为 ;若使用 10,则会贡献 ,两边求和就是递推式。一般地,这种递推都可以写成组合数的和。现在我以这个题为例讲解。
对于任意的 ,我们把结果分为三个部分:
- 处贡献的 之和。
- 处贡献的 之和。
- 递推过程中积累的 之和。
容易发现第二部分的贡献就是第一部分贡献的 倍,所以这里只讲解第一部分。
为了便于叙述,先给出一个定义:设一个数列满足各项均为同一个数,则称其为 阶等差数列;若一个数列满足其差分数列为 阶等差数列,则原序列为 阶等差数列。特别地,若一个数列各项均为 ,则称其为 阶基等差数列,若对一个 阶等差数列进行 阶差分后可得到 阶基等差数列,则称原数列为 阶基等差数列。
根据定义,我们容易得出 阶基等差数列的各项其实就是对 阶基等差数列的对应项求和。
为了便于叙述,先给出一个定义:设一个数列满足各项均为同一个数,则称其为 阶等差数列;若一个数列满足其差分数列为 阶等差数列,则原序列为 阶等差数列。特别地,若一个数列各项均为 ,则称其为 阶基等差数列,若对一个 阶等差数列进行 阶差分后可得到 阶基等差数列,则称原数列为 阶基等差数列。
根据定义,我们容易得出 阶基等差数列的各项其实就是对 阶基等差数列的对应项求和。
关于差分
设一个数列 的差分数列 为 有 ,特别规定 。
阶差分就是进行 次差分操作。
阶差分就是进行 次差分操作。
回到正题。根据 的一般递推式, 处的贡献应为 和 时 处的贡献之和。我们考察 ,展开至 时容易发现 接受的 处的贡献就等于 的 处的贡献之和。我们观察到 处各项实际上就是 阶基等差数列的对应各项和,归纳易得 处 的贡献就是 阶基等差数列的前 项和,或者说,就是 阶基等差数列的第 项。
那么怎么求呢?我们考虑组合意义。
我们令 表示 阶基等差数列第 项,那么有:
我们注意到:令 表示满足 且 有 为非负整数的有序 元组 的数量也是成立的!
那么怎么求呢?我们考虑组合意义。
我们令 表示 阶基等差数列第 项,那么有:
我们注意到:令 表示满足 且 有 为非负整数的有序 元组 的数量也是成立的!
为何这是成立的?
对于边界情况,显然是对的。
一般的情况中,我们注意到 。而对于我们转化后的组合意义,这恰是其转移式。具体地:
一般的情况中,我们注意到 。而对于我们转化后的组合意义,这恰是其转移式。具体地:
- 对于有序 元组,我们考虑 的值。
- 若 ,则有 ,故产生 的贡献。
- 若 ,则我们考虑有序 元组 ,我们只需统计它的数量。贡献就是 。
- 两者相加就是总数。
那么我们考虑求解转化后的组合意义。这是一个经典的问题,我们可以考虑“插板法”,容易得出 。
关于这个问题的推导
我们将 分成 个 相加。那么 之间会产生 个“间隔”,在“间隔”中我们可以放“板”。一个“板”会将其所在的极大 块分为 份,即生成了两段 的和。
对于原问题,因为 可以取到 且要求有序,所以“板”可以插在第一个 的左侧或最后的 右侧(即开头结尾),这样就有了 个“间隔”。
同时,我们关注到多个“板”可以同时放在一个“间隔”中,表示 中存在一段 ,所以我们不妨考虑增加若干个“通配间隔”,用于容纳这些重复的“板”。因为“板”一共需要 个(拆成 段,即 个元),所以我们需要 个“通配间隔”,与原来的间隔合起来可得总共有 个“间隔”,那么答案就显而易见了:共 个放“板”的方式,也就是这么多组 的可行解。此时对于边界情况也是没问题的。
对于原问题,因为 可以取到 且要求有序,所以“板”可以插在第一个 的左侧或最后的 右侧(即开头结尾),这样就有了 个“间隔”。
同时,我们关注到多个“板”可以同时放在一个“间隔”中,表示 中存在一段 ,所以我们不妨考虑增加若干个“通配间隔”,用于容纳这些重复的“板”。因为“板”一共需要 个(拆成 段,即 个元),所以我们需要 个“通配间隔”,与原来的间隔合起来可得总共有 个“间隔”,那么答案就显而易见了:共 个放“板”的方式,也就是这么多组 的可行解。此时对于边界情况也是没问题的。
我们要求的是 ,带入得 ,这便是第一部分的贡献。
类似地,第二部分的贡献为 。
接下来我们考虑第三部分。我们设 表示 所包含的 的贡献的总数。那么,我们容易得出递推式:
类似地,第二部分的贡献为 。
接下来我们考虑第三部分。我们设 表示 所包含的 的贡献的总数。那么,我们容易得出递推式:
注: 表示 或 ,即 间至少有一个成立。
那么实际上 中 的贡献就是 。
我们还是考虑赋予其组合意义。容易发现 实际上就是从平面上任意的 仅向右、向下走到 的不同路径数量。
我们还是考虑赋予其组合意义。容易发现 实际上就是从平面上任意的 仅向右、向下走到 的不同路径数量。
关于这个组合意义的正确性
这个组合意义正确性的证明是显然的,因为走一步到 只有两个方法:从 或 过来。所以将 和 相加。同时我们发现这么处理会漏掉 走到 本身的方案,所以加 即可。
然后我们考虑翻转整个平面,使 变为 ,那么对应地,我们就要求从任意的 仅向上、向左走到 的不同路径数量。容易得出为 。
关于路径计数
从 仅向上、向左走到 的方案计数我们可以考虑其每步的变化量。
容易发现其必向上 步,向左 步,不同的路径实际上是不同的变化量的组合。
那么此时其方案数就有 。
容易发现其必向上 步,向左 步,不同的路径实际上是不同的变化量的组合。
那么此时其方案数就有 。
我们把我们推出的求和式子带入递推式:
那么我们容易得到 对 的贡献就是 ,求和可得:
带入一开始的式子,则有:
因为本题数据范围很大、模数很小,所以我们要使用 Lucas 定理辅助计算。
另外就是本题比较卡常,所以
Record
Code:
CPP那么我们容易得到 对 的贡献就是 ,求和可得:
带入一开始的式子,则有:
因为本题数据范围很大、模数很小,所以我们要使用 Lucas 定理辅助计算。
另外就是本题比较卡常,所以
cin 可能会被卡。Record
Code:
//luogu paste jo5j6ogx
cst ll p=18888913;
int t;
ll n,m,fac[p+10],inv[p+10],ans,r;
il ll C(int n,int m){
if(m<0||m>n) ret 0;
ret fac[n]*inv[n-m]%p*inv[m]%p;
}
il ll Lucas(ll n,ll m){
if(m<0||m>n) ret 0;
if(n>p||m>p) ret C(n%p,m%p)*Lucas(n/p,m/p)%p;
ret C(n,m);
}
int main(void){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
fac[0]=1;
for(ll i=1;i<p;i++) fac[i]=fac[i-1]*i%p;
inv[p-1]=pinv(fac[p-1],p);
for(ll i=p-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%p;
t=read<int>();
while(t--){
n=read<ll>();m=read<ll>();
if(n>m) continue;
ans=msub(madd(madd(4ll*Lucas(m-1,n-1)%p,2ll*Lucas(m-1,n-2)%p,p),Lucas(m-1,n),p),2,p);
r^=ans;
}
write(r);
ret 0;
}
后记
写得很详细呢,就当是给组合数学初学者的一份入门教程了。
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...