题目链接: http://codeforces.com/problemset/problem/551/D
题意:
你现在需要构造一个长为 n n n 的数组 a a a ,使得 ( a 1 a n d a 2 ) ∣ ( ( a 2 a n d a 3 ) ∣ . . . ∣ ( a n − 1 a n d a n ) ) = k (a_1and a_2)|((a_2and a_3)|...|(a_{n-1}and a_n))=k (a1anda2)∣((a2anda3)∣...∣(an−1andan))=k ,且 a i < 2 l a_i<2^l ai<2l ,问你能构造出多少个这样的数组 a a a。
做法:
我们将 k k k 用二进制进行拆分,对于某一位 i i i ,假设
- k k k 的这一位为 0 0 0,那么就表示这一位在数组中没有两个连续的 1 1 1 。
- k k k 的这一位为 1 1 1,那么就表示这一位在数组中至少出现一次两个连续的 1 1 1 。
那么假设我们要算没有出现两个连续的 1 1 1 的情况数有几个,对于 a 1 a_1 a1 来说,这一位可以是 0 0 0 或者 1 1 1 ,如果是 0 0 0 ,那么下一个可以出现 1 1 1 和 0 0 0 ,否则下一个只能出现 0 0 0 。
用矩阵表示即
最后得出的两个数相加即为不能出现连续两个
1
1
1 的方案数,另一种情况只需要
2
n
2^n
2n 减去这个数量即可。
代码
#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;
}