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; }