題目鏈接: http://codeforces.com/problemset/problem/551/D
題意:
你現在需要構造一個長爲 的數組 ,使得 ,且 ,問你能構造出多少個這樣的數組 。
做法:
我們將 用二進制進行拆分,對於某一位 ,假設
- 的這一位爲 ,那麼就表示這一位在數組中沒有兩個連續的 。
- 的這一位爲 ,那麼就表示這一位在數組中至少出現一次兩個連續的 。
那麼假設我們要算沒有出現兩個連續的 的情況數有幾個,對於 來說,這一位可以是 或者 ,如果是 ,那麼下一個可以出現 和 ,否則下一個只能出現 。
用矩陣表示即
最後得出的兩個數相加即爲不能出現連續兩個 的方案數,另一種情況只需要 減去這個數量即可。
代碼
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u,v) for(int i=head[u],v=to[i];~i;i=nex[i],v=to[i])
using namespace std;
typedef long long ll;
const int maxn=305;
const int maxm=100005;
ll n,k,mod,l;
struct mat{
ll a[5][5];
void Clear(){
memset(a,0,sizeof(a));
rep(i,1,2){
a[i][i]=1;
}
}
};
mat mul(mat a,mat b){
mat ans;
memset(ans.a,0,sizeof(ans.a));
rep(i,1,2)
rep(j,1,2)
rep(k,1,2)
ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
return ans;
}
ll quick(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
mat Quick(mat a,ll k){
mat ans;
ans.Clear();
while(k){
if(k&1) ans=mul(ans,a);
a=mul(a,a);
k/=2;
}
return ans;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&k,&l,&mod);
ll num0=0,num1=0;
for(ll i=0;i<min(l,63ll);i++){
if(k&(1ll<<i))num1++;
else num0++;
}
if(l==64) num0++;
if(l<63){
if(k>=(1ll<<l)){
return 0*printf("0\n");
}
}
mat now,tmp;
memset(now.a,0,sizeof(now.a));
now.a[1][1]=now.a[1][2]=now.a[2][1]=1;
memset(tmp.a,0,sizeof(tmp.a));
tmp.a[1][1]=tmp.a[2][1]=1;
mat fin=Quick(now,n-1);
fin=mul(fin,tmp);
ll no_s1=(fin.a[1][1]+fin.a[2][1])%mod;
ll yes_s1=(quick(2ll,n)+mod-no_s1)%mod;
ll ans=(quick(no_s1,num0)%mod*quick(yes_s1,num1)%mod)%mod;
printf("%lld\n",ans);
return 0;
}