Codeforces 551 D. GukiZ and Binary Operations

Description

给出\(n,k,l\),问有多少种构造长度为\(n\)的序列\(a\)方法,使\(a_i<l\)\((a_1 \ \text{and} \ a_2) \ \text{or} \ (a_2 \ \text{and} \ a_3) \ \text{or} \cdots \text{or} \ (a_{n-1} \ \text{and} \ a_n)=k\)

\(n,k \leq 2^{60},l \leq 60\)

Solution

位运算一定要按位做!!!

按位做之后,问题就由非常大的整数问题转化为了01的问题。

那么考虑\(k=0\)时,不能出现有相邻的\(a_i\)都是1。因此我们可以\(DP\)一下。定义\(f_{i,0}\)表示第\(i\)位填0的前\(i\)位没有相邻1的方案数,\(f_{i,1}\)同理。有:

\[\left\{ \begin{aligned} f_{i,0} & =f_{i-1,0}+f_{i-1,1} \\ f_{i,1} & =f_{i-1,0}\\ \end{aligned} \right. \]

因此\(f_{i,0}=f_{i-1,0}+f_{i-2,0}\)。答案是\(f_{n,0}+f_{n,1}=f_{n,0}+f_{n-1,0}=f_{n+1,0}\)。而\(f_{i,0}\)是线性递推式,是可以矩阵快速幂优化的(事实上这就是一个斐波那契数列,以下斐波那契数列记为\(f_i\))。

接下来考虑\(k=1\),即需要存在一对连续1。我们可以借鉴\(\text{CF559C}\)的思想——枚举第一个连续1,那么之前不能存在连续1,后面可以随便。那么有

\[ans=\sum\limits_{i=1}^{n-1}f_{i-1} \times 2^{n-i-1} \]

处理这个问题可以引入一个新的函数\(g\),定义

\[g_n=\sum\limits_{i=1}^{n-1}f_{i-1} \times 2^{n-i-1} \]

那么有

\[g_i=2 \times g_{i-1}+f_{i-1} \]

考虑构造矩阵来算\(g\)。有

\[\begin{bmatrix} g_{i-1} & f_{i-2} & f_{i-1} \end{bmatrix} \times \begin{bmatrix} 2 & 0 & 0\\ 0 & 0 & 1\\ 1 & 1 & 1\\ \end{bmatrix} \text{=} \begin{bmatrix} g_{i} & f_{i-1} & f_{i} \end{bmatrix} \]

Discovery(戴睿同学的做法)

事实上,我们还可以用容斥来解决\(k=1\)的问题。总共有\(2^n\)中方案,接下来减去没有连续\(1\)的方案数(也就是\(k=0\)的答案)。简单多了= =。

但这让我们发现了一个关于斐波那契数列(下标从0开始)的恒等式:

\[\sum\limits_{i=1}^{n-1}f_{i-1} \times 2^{n-i-1}=2^n-f_{n+1} \]

要证明这个恒等式,我们可以将等式两边分别看做两个生成函数的系数,如果两个生成函数相等则恒等式成立。

\(\sum\limits_{i=1}^{n-1}f_{i-1} \times 2^{n-i-1}= \sum\limits_{i=0}^{n-2}f_{i} \times 2^{n-i-2}\)可以看做斐波那契数列与等比数列的卷积,那么左边的生成函数封闭表达式即为

\[\dfrac{1}{1-x-x^2} \cdot \dfrac{1}{1-2x} \]

右边是系数相减,可以看做两个生成函数相减。\(2^n\)的生成函数是\(\dfrac{1}{1-2x}\)\(f_{n+1}\)的生成函数是\(\dfrac{\dfrac{1}{1-x-x^2}-f_0}{x}=\dfrac{1+x}{1-x-x^2}\)。那么右侧的生成函数是\(\dfrac{1}{1-2x}-\dfrac{1+x}{1-x-x^2}\),化简得

\[\dfrac{x^2}{(1-x-x^2)(1-2x)} \]

由于左边系数的下标是\(n-2\),因此左边的生成函数要乘以\(x^2\)。因此左右相等,因此命题得证。

推斐波那契数列的通项公式

\(F(x)=f_0+f_1x+f_2x^2+f_3x^3+ \cdots\)

\(xF(x)=f_0x+f_1x^2+f_2x^3+f_3x^4+ \cdots\)

\(x^2F(x)=f_0x^2+f_1x^3+f_2x^4+f_3x^5+ \cdots\)

\(F(x)-xF(x)=f_0+f_0x^2+f_1x^3+\cdots=x^2F(x)+1\)

\(F(x)=\dfrac{1}{1-x-x^2}\)

考虑将\(F(x)\)的封闭表达式表达成\(\dfrac{a_1}{1-a_2x}\)这样的等比数列的形式。

考虑将\(F(x)\)进行裂项:

\(F(x)=\dfrac{a}{1-\phi_1x} + \dfrac{b}{1-\phi_2x}\)

其中可以解得\(\phi_1=\dfrac{\sqrt 5+1}{2},\phi_2=\dfrac{1-\sqrt 5}{2}\)

\(a(1-\phi_2x)+b(1-\phi_1x)=1\)恒成立

所以

\[\left\{ \begin{aligned} &a\phi_2+b\phi_1 = 0\\ &a+b=1\\ \end{aligned} \right. \]

解得

\[\left\{ \begin{aligned} &a=\dfrac{\phi_1}{\phi_1-\phi_2}\\ &b=-\dfrac{\phi_2}{\phi_1-\phi_2}\\ \end{aligned} \right. \]

q'z
所以

\[F(x)=\dfrac{1}{\phi_1-\phi_2}(\dfrac{\phi_1}{1-\phi_1x}-\dfrac{\phi_2}{1-\phi_2x}) \]

因此系数是\(f_n=\dfrac{1}{\phi_1-\phi_2}(\phi_1^n-\phi_2^n)\)

\[f_n=\dfrac{1}{\sqrt 5}((\dfrac{\sqrt 5+1}{2})^n+(\dfrac{\sqrt 5-1}{2})^n) \]

Code

/*DennyQi 2019*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
const int N = 100010;
const int P = 998244353;
const int INF = 0x3f3f3f3f;
inline int read(){
    int x(0),w(1); char c = getchar();
    while(c^'-' && (c<'0' || c>'9')) c = getchar();
    if(c=='-') w = -1, c = getchar();
    while(c>='0' && c<='9') x = (x<<3)+(x<<1)+c-'0', c = getchar(); return x*w;
}
int n,k,l,m,Fn,ans,cur,y;
inline int mul(const int& a, const int& b){ return 1ll*a*b%m; }
inline int add(const int& a, const int& b){ return (a+b>=m)?a+b-m:a+b; }
inline int sub(const int& a, const int& b){ return (a-b<0)?a-b+m:a-b; }
struct matrix{
    int a[2][2];
    matrix operator * (const matrix& X) const{
        matrix res;
        for(int i = 0; i < 2; ++i){
            for(int j = 0; j < 2; ++j){
                res.a[i][j] = 0;
                for(int k = 0; k < 2; ++k){
                    res.a[i][j] = add(res.a[i][j],mul(a[i][k],X.a[k][j]));
                }
            }
        }
        return res;
    }
};
matrix x,res;
inline int ksm(int x, int y){
    int res = 1;
    while(y>0){
        if(y&1) res = mul(res,x);
        y >>=1, x = mul(x,x);
    }
    return res;
}
#undef int
int main(){
#define int long long
    // freopen("file.in","r",stdin);
    n = read(), k = read(), l = read(), m = read();
    if(l<=62&&(1ll<<l)<=k){
        puts("0");
        return 0;
    }
    res.a[0][0] = res.a[1][1] = 1;
    x.a[0][1] = x.a[1][0] = x.a[1][1] = 1;
    y = n;
    while(y>0){
        if(y&1) res = res*x;
        y >>= 1, x = x*x;
    }
    Fn = add(res.a[0][1],res.a[1][1]);
    ans = 1;
    for(int i = 1; i <= l; ++i){
        cur = (k&1);
        k >>= 1;
        if(cur == 0){
            ans = mul(ans,Fn);
        }else{
            ans = mul(ans,sub(ksm(2,n),Fn));
        }
    }
    printf("%lld\n",ans%m);
    return 0;
}
posted @ 2020-12-04 22:16  DennyQi  阅读(78)  评论(0编辑  收藏  举报