CF1605F PalindORme 解题报告

CF1605F PalindORme 解题报告

0.前置芝士

二项式反演:

f ( n ) = ∑ 0 ≤ i ≤ n ( n i ) g ( i ) ⟹ g ( n ) = ∑ 0 ≤ i ≤ n ( − 1 ) n − i ( n i ) f ( i ) f(n)=\sum_{0 \le i \le n}{\dbinom{n}{i}g(i)}\\ \Longrightarrow g(n)=\sum_{0 \le i \le n}{(-1)^{n-i}\dbinom{n}{i}f(i)} f(n)=0in(in)g(i)g(n)=0in(1)ni(in)f(i)

证明可参考这位大佬

1.题意简述

定义合法序列为重排后可满足:对于任意相同长度的前缀和后缀,它们的按位或和相等的序列。询问长度为n,值域为 [ 0 , 2 k − 1 ] [0,2^k-1] [0,2k1] 的合法序列序列的个数。

(注意:我这里的合法序列序列和题中的 PalindORme 序列的定义不同)

2.判定

在解决对合法序列的计数之前,我们至少要先掌握它的判定。

根据定义,我们首先可以发现, a 1 = a n a_1 = a_n a1=an

进一步,
a 2 ∣ ( a 1 ) = a n − 1 ∣ ( a n ) a 3 ∣ ( a 1 ∣ a 2 ) = a n − 2 ∣ ( a n ∣ a n − 1 ) a 4 ∣ ( a 1 ∣ a 2 ∣ a 3 ) = a n − 3 ∣ ( a n ∣ a n − 1 ∣ a n − 2 ) ⋯ a_2\mid(a_1)=a_{n-1}\mid(a_n)\\ a_3\mid(a_1\mid a_2)=a_{n-2}\mid(a_n \mid a_{n-1})\\ a_4\mid(a_1\mid a_2 \mid a_3)=a_{n-3}\mid(a_n \mid a_{n-1} \mid a_{n-2})\\ \dotsb a2(a1)=an1(an)a3(a1a2)=an2(anan1)a4(a1a2a3)=an3(anan1an2)
于是我们发现了一个判定合法序列的算法:

记前缀 or 值为 v a l val val

如果能从序列中找到两个数 x x x, y y y, 使得 x ∣ v a l = y ∣ v a l x \mid val = y \mid val xval=yval,就令 v a l = v a l ∣ x val = val \mid x val=valx,并删去 x x x, y y y

如果最后序列中剩下的数不超过1个,说明这个序列是合法序列,否则这个序列就是非法序列。

3.非法序列

我们发现,在经过了上述算法之后,非法序列所剩余的序列一定满足:删去 v a l val val所含有的二进制位后序列中的任意数互不相同且不为0。

我们把序列内任意数互不相同且不为0的序列称为 artalter 序列,那么任何一个非法序列都由一个合法序列和一个删去 v a l val val所含有的二进制位后为 artalter 序列的序列构成。

也就是说,我们在合法序列和非法序列之间建立了联系,这道题就有了突破口。

4.求解

说了这么多,我们终于进入正题了。

定义 A n , m A_{n,m} An,m为元素个数为 n n n,有 m m m 个二进制位的序列个数

定义 B n , m B_{n,m} Bn,m为元素个数为 n n n,有 m m m 个二进制位的 artalter 序列个数

定义 C n , m C_{n,m} Cn,m为元素个数为 n n n,有 m m m 个二进制位的不合法序列个数

根据乘法原理我们知道元素个数为 n n n,二进制位数小于等于 m m m 的序列个数为 ( 2 m ) n (2^{m})^{n} (2m)n

根据简单的组合知识,
( 2 m ) n = ∑ 0 ≤ i ≤ m ( m i ) A n , i ⟹ A n , m = ∑ 0 ≤ i ≤ m ( − 1 ) m − i ( m i ) ( 2 i ) n (2^{m})^{n}=\sum_{0 \le i \le m}{\dbinom{m}{i}A_{n,i}}\\ \Longrightarrow A_{n,m}=\sum_{0 \le i \le m}{(-1)^{m-i}\dbinom{m}{i}(2^{i})^{n}} (2m)n=0im(im)An,iAn,m=0im(1)mi(im)(2i)n

我们还知道元素个数为 n n n,二进制位数小于等于 m m m 的 artalter 序列个数为 ( 2 m − 1 ) n ‾ (2^{m}-1)^{\underline{n}} (2m1)n
(因为代表排列的 A A A 已被占用,所以这里使用了下降幂)

( 2 m − 1 ) n ‾ = ∑ 0 ≤ i ≤ m ( m i ) B n , i ⟹ B n , m = ∑ 0 ≤ i ≤ m ( − 1 ) m − i ( m i ) ( 2 i − 1 ) n ‾ (2^{m}-1)^{\underline{n}}=\sum_{0 \le i \le m}{\dbinom{m}{i}B_{n,i}}\\ \Longrightarrow B_{n,m}=\sum_{0 \le i \le m}{(-1)^{m-i}\dbinom{m}{i}(2^{i}-1)^{\underline{n}}} (2m1)n=0im(im)Bn,iBn,m=0im(1)mi(im)(2i1)n

最后,我们求出 C C C

C n , m = ∑ 0 ≤ i < n ∑ 0 ≤ j < m ( n i ) ( m j ) ( A i , j − C i , j ) ( 2 j ) n − i B n − i , m − j C_{n,m}=\sum_{0 \le i < n}\sum_{0 \le j < m}{\dbinom{n}{i}\dbinom{m}{j}(A_{i,j}-C_{i,j})(2^j)^{n-i}B_{n-i,m-j}} Cn,m=0i<n0j<m(in)(jm)(Ai,jCi,j)(2j)niBni,mj
这个式子的组合意义是:枚举有n个元素,有 m m m 个二进制位的非法序列中,最大的有i个元素,有 j j j 个二进制位的合法序列。

( n i ) ( m j ) \dbinom{n}{i}\dbinom{m}{j} (in)(jm)代表从n个元素中选取i个元素,从m个二进制位中选取j个为为合法序列所含有的二进制位。

( A i , j − C i , j ) (A_{i,j}-C_{i,j}) (Ai,jCi,j) 表示符合要求的合法序列的个数

( 2 j ) n − i (2^j)^{n-i} (2j)ni 表示在所余序列中val包含的二进制位可以任取。

B n − i , m − j B_{n-i,m-j} Bni,mj 表示在所余序列中val包含的二进制位以外的二进制位构成一个artalter序列

A n s = ∑ 0 ≤ i ≤ m ( m i ) ( A n , i − C n , i ) Ans=\sum_{0 \le i \le m}\dbinom{m}{i}(A_{n,i}-C_{n,i}) Ans=0im(im)(An,iCn,i)

5.代码

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL f[105][105];
int A[105][105], C[105][105], B[105][105];
LL fsp(LL a, LL b, LL mod)
{
    LL s = 1;
    while (b)
    {
        if (b & 1)
            s = (s * a) % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return s;
}
LL low(LL x,LL y,LL mod){
    LL s=1;
    for(int i=x;i>=x-y+1;i--)s*=i,s%=mod;
    return s;
}
int main()
{
    LL n, m, mod;
    cin >> n >> m >> mod;
    for (int i = 0; i <= 100; i++)
    {
        f[i][0] = f[i][i] = 1;
        for (int j = 1; j < i; j++)
        {
            f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % mod;
        }
    }
    for (int i = 0; i <= n; i++)
    {
        for (int j = 0; j <= m; j++)
        {
            for(int k=0;k<=j;k++){
                A[i][j]+=fsp(-1,j-k,mod)*f[j][k]%mod*fsp(2,k*i,mod)%mod;
                B[i][j]+=fsp(-1,j-k,mod)*f[j][k]%mod*low(fsp(2,k,mod)-1,i,mod)%mod;
                A[i][j]%=mod,B[i][j]%=mod;
            }
        }
    }
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int i_=0;i_<i;i_++){
                for(int j_=0;j_<j;j_++){
                    if(n%2==1&&i==n&&i_==n-1){
                        continue;
                    }
                    C[i][j]+=f[i][i_]*f[j][j_]%mod*fsp(2,j_*(i-i_),mod)%mod*B[i-i_][j-j_]%mod*(A[i_][j_]-C[i_][j_]+mod)%mod;
                    C[i][j]%=mod;
                }
            }
        }
    }
    LL ans=0;
    for(int i=0;i<=m;i++){
        ans+=f[m][i]*(A[n][i]-C[n][i])%mod;
    }
    cout<<(ans%mod+mod)%mod;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值