题目链接:
题目大意:
有一个数组,(a1&&a2)|| ....... (an-1&&an)=k,ai<=2^l,求取这样的数组的数量%m
题目分析:
作为一个弱渣,这道题调了一上午才过,简直了。。。。
其实思路很简单,首先将k化为二进制数,那么如果k的某一个二进制位是0,那么也就是这个数组的相邻数位不可能全是1
因此我们得到如下的递推式:dp[i] = dp[i-1]+dp[i-2],当第i位放0时,那么有dp[i-1]中情况,如果第i位放1,那么第i-1位必须放0,所以有dp[i-2]种情况,正好可以看出是斐波那契数列,那么我们可以通过矩阵快速幂快速求取
然后看为1的情况,dp[i] = 2^n-1 + dp[i-1] ,表示当前两个数位都是1,那么其他的随便放,也就是2^n-1,如果当前两个数位不全是1,那么前面必有一个是1,所以是dp[i-1]种,但是这么做的话,递推起来又过于麻烦,可以突然想到只有1,0两种情况,一共的情况数是2^n,那么减掉0的情况不就是1的情况了吗!!!!被自己蠢哭了。。。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAX 2
using namespace std;
typedef long long LL;
LL m,n,l,k;
struct Matrix
{
LL a[MAX][MAX];
};
void reset ( Matrix& m )
{
memset ( m.a , 0 , sizeof ( m.a ) );
}
void set ( Matrix& m )
{
memset ( m.a , 0 , sizeof ( m.a ) );
for ( int i = 0 ; i < 2 ; i++ )
m.a[i][i] = 1;
}
Matrix multi ( Matrix m1 , Matrix m2 )
{
Matrix ret;
reset ( ret );
for ( int i = 0 ; i < 2 ; i++ )
for ( int j = 0 ; j < 2 ; j++ )
if ( m1.a[i][j] )
for ( int k = 0 ; k < 2 ; k++ )
ret.a[i][k] = (ret.a[i][k] + m1.a[i][j] * m2.a[j][k]%m)%m;
return ret;
}
Matrix quickMulti ( Matrix m1 , LL n )
{
Matrix ret;
set ( ret );
while ( n )
{
if ( n&1 ) ret = multi ( ret , m1 );
m1 = multi ( m1 , m1 );
n >>= 1;
}
return ret;
}
LL pow ( LL x , LL n )
{
LL ret = 1;
while ( n )
{
if ( n&1 ) ret = ret*x%m;
x = x*x%m;
n >>= 1;
}
return ret;
}
Matrix a,b;
LL ans;
void init ( )
{
reset ( b );
b.a[0][0] = b.a[0][1] = b.a[1][0] = 1;
ans = 1;
}
int main ( )
{
while ( cin >> n >> k >> l >> m )
{
init();
b = quickMulti ( b , n+1 );
LL k1 = b.a[0][0];
LL k2 = ((pow (2,n)-k1)%m+m)%m;
int cnt = 0;
if ( m == 1 )
{
puts ("0");
continue;
}
if ( l < 63 && (1LL<<l) <= k )
{
puts ( "0" );
continue;
}
if ( l == 0 )
{
if ( k == 0 ) puts("1");
else puts("0");
continue;
}
while ( k )
{
if ( k&1 ) ans = ans*k2%m;
else ans = ans*k1%m;
k >>= 1;
cnt++;
}
while ( cnt++ < l )
ans = ans*k1%m;
ans = (ans%m+m)%m;
cout << ans << endl;
}
}