51nod网站
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1436
这道题的方法很简单,关键在于这道题有很多角度都能切入思考,能不能快速想到合适的解法。
方法步骤:
将k化为二进制,则对应bit位置若为0,则a1,a2,...,an相应的bit位设为b1,b2,...,bn。相邻2个bi不能同时为1。考虑这样情况的总数。
既然不能出现连续的1。设方法总数为f(n),bn为1时,b(n-1)为0,此时方法数为f(n-2)。当bn为0时,方法数为f(n-1)。
则利用数学归纳法,很容易得到:f(n) = f(n-1)+f(n-2)。初始条件可令f(0)=1,f(1)=2,从而有f(2)=3。
最终答案
其中count为k数字二进制里1的数量。
最终求解的时候,f(n)可以用矩阵快速幂log(n)求解,并且可以发现是菲波拉契数列。并且所有涉及到的幂次方都可以用快速幂进行求解。
最后要注意:
1.取余要注意最后结果是负数的话要变为正数
2.当2^l-1比k小的时候,方案数为0。
#include <stdio.h>
__int64 n, k, tmp, l, m, myresult, tmp1, mycount, fn, mymaxsize;
#define mymaxsize1 9223372036854775807 //2^63-1
void MyMultiply(__int64 A[2][2], __int64 B[2][2], __int64 C[2][2])
{
int i, j;
__int64 result[2][2], tmp;
result[0][0] = result[0][1] = result[1][0] = result[1][1] = 0;
for (i = 0; i < 2; ++i)
{
for (k = 0; k < 2; ++k)
{
tmp = A[i][k];
for (j = 0; j < 2; ++j)
{
result[i][j] = (result[i][j] + (tmp * B[k][j]) % m) % m;
// result[i][j] = result[i][j] + (A[i][k] * B[k][j]);
}
}
}
for (i = 0; i < 2; ++i)
{
for (j = 0; j < 2; ++j)
{
C[i][j] = result[i][j];
}
}
}
void GetMyPowerMod(__int64 x, __int64 y, __int64 m, __int64 &result)
{
result = 1;
__int64 tmp1=x;
while (y)
{
if (y & 1) { result = (result * tmp1) % m; }
y >>= 1;
tmp1 = (tmp1 * tmp1) % m;
}
}
void Bit1Count(__int64 x, __int64 &result)
{
result = 0;
for (result = 0; x; ++result)
{
x &= (x - 1);
}
}
void GetFib(__int64 n, __int64 &result)
{
n += 2;
__int64 resultarray[2][2], C[2][2];
resultarray[0][0] = resultarray[1][1] = 1;
resultarray[0][1] = resultarray[1][0] = 0;
C[0][1] = C[1][1] = C[1][0] = 1; C[0][0] = 0;
while (n)
{
if (n & 1)
{
MyMultiply(resultarray, C, resultarray);
}
n >>= 1;
MyMultiply(C, C, C);
}
result = resultarray[0][1];
}
int main()
{
scanf("%lld%lld%lld%lld", &n, &k, &l, &m);
if (l < 63)
{
GetMyPowerMod(2, l, mymaxsize1, tmp1);
if (tmp1<=k)
{
printf("0");
return 0;
}
}
Bit1Count(k, mycount);
GetFib(n, fn);
GetMyPowerMod(2, n, m, tmp1);
GetMyPowerMod(tmp1 - fn, mycount, m, myresult);
GetMyPowerMod(fn, l - mycount, m, tmp);
myresult = (myresult * tmp) % m;
if (myresult<0)
{
myresult += m;
}
printf("%lld", myresult);
for (int i = 1; i <= 10; ++i)
{
scanf("%lld", &n);
}
return 0;
}