http://codeforces.com/contest/551/problem/D
给你 n,l,k,m;
题意:你可以任意挑选小于2^l的n个数,让它们以这个公式
计算得到k;要使得 得到的k与给出的k相等,问你有多少种方案数,答案取余m。
首先我们看如何得到k;
我们需要构造n个数
我们先把这n个数转为 L位 二进制数,得到一个0 1 矩阵:
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
同时我们也把k转为 L位 二进制数:
000000000000111111
我们可以知道,要使得k的第i位为0,则要求 n个数中,不能有相邻的两个数 的 【第i 位】都为1 (如果相邻为1,根据公式推得 k的第i位最终必为 1);
假如我们能求出,这个使ki位0的方案数,我们设有x种情况符合该条件;
并且,由于这n个数的第i位,总共最多只有2的n次方总情况。(每一个数的第i位有2种情况,n个数是2^n种情况)
同样的,如果要使得k的第i位为1,其情况有y=2^n-x种
注意:
1、如果这n个数的第i位确定了是111100(从上到下),他与第i-1位完全没关系,相互独立,也就是说,k的每一位我们都可以独立计算,位与位之间并没有关联。 (之前一直误以为是选n个数,如果是选的话,选了n个数使得k的第i位为0,第i-1,i-2,i-3位都会被确定下来,那就无法做下去了。但是我们是构造n个数,构造的话,每一bit之间是没影响的)
接下来是怎么计算 在n 个数 的情况下 ,经过公式计算 能得到 k的第i位为0,也就是n个数在第i位里【没有相邻的两个1】存在;
可发现,这个答案为f(n)=f(n-1)+f(n-2); 恰好是斐波那契数列,由于n太大 10^18//这个证明迟点再写
我们可以用矩阵快速幂来计算 得到 这个x
然后对k的每一位判断,如果是0,则ans*=x;
如果是1,则ans*=y;
最后输出 ans%mod;
/***************************************************************
为什么 一个n长度的零壹数组,每个位置元素随意,要求 不能存在相邻的两个1,求多少种排列,的答案是斐波那契数列?
证明:
如果n长度的串第一位是0 ,那么后面随便跟,n-1长度的合法串方案书就是f(n-1)
如果n长度的串第一位是1,那么第二位肯定是0,后面随便跟,n-2长度的合法串方案书就是f(n-2)
第一位只有这两种情况,就是f(n)=f(n-1)+f(n-2)
****************************************************************/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
struct martix
{
__int64 m[2][2];
};
martix unit_matrix;
__int64 mod;
__int64 len=2;
martix mul(martix a,martix b)
{
martix ret;
__int64 i,j,k;
for (i=0;i<len;i++)
{
for (j=0;j<len;j++)
{
ret.m[i][j]=0;
for (k=0;k<len;k++)
{
ret.m[i][j]+=a.m[i][k]*b.m[k][j];
ret.m[i][j]%=mod;
}
}
}
return ret;
}
martix pow_m(martix a,__int64 b)
{
martix ret= unit_matrix;
while(b)
{
if (b&1)
ret=mul(ret,a);
a=mul(a,a);
b>>=1;
}
return ret;
}
__int64 pow_m_int(__int64 a,__int64 b)
{
__int64 ret= 1;
while(b)
{
if (b&1)
ret=(ret*a)%mod;
a=a*a%mod;
b>>=1;
}
return ret%mod;
}
int main()
{
__int64 n,k,l;
__int64 i,j;
scanf("%I64d%I64d%I64d%I64d",&n,&k,&l,&mod);
for(i = 0; i < 2; i++)
for(j = 0; j < 2; j++)
unit_matrix.m[i][j] = 0;
for(i = 0; i < 2; i++) //初始化单位矩阵
unit_matrix.m[i][i] = 1;
if ( pow(2.0,l)<= k)
{
printf("0\n");
return 0;
}
martix x;
x.m[0][0]=1; x.m[0][1]=1;
x.m[1][0]=1; x.m[1][1]=0;
x=pow_m(x,n+1); //n个数的该位有ans种情况使得k的该位为0
__int64 xx=x.m[0][0]%mod;
__int64 one=(pow_m_int(2,n)-xx)%mod;
if (one<0)
one+=mod;
__int64 sum=1;
for (i=0;i<l;i++)
{
if (k&1)
sum=(sum*one)%mod;
else
sum=sum*xx%mod;
k>>=1;
}
printf("%I64d\n",sum%mod);
return 0;
}