CF551D GukiZ and Binary Operations 矩陣快速冪

題目鏈接: http://codeforces.com/problemset/problem/551/D

題意:

你現在需要構造一個長爲 nn 的數組 aa ,使得 (a1anda2)((a2anda3)...(an1andan))=k(a_1and a_2)|((a_2and a_3)|...|(a_{n-1}and a_n))=k ,且 ai<2la_i<2^l ,問你能構造出多少個這樣的數組 aa

做法:

我們將 kk 用二進制進行拆分,對於某一位 ii ,假設

  • kk 的這一位爲 00,那麼就表示這一位在數組中沒有兩個連續的 11
  • kk 的這一位爲 11,那麼就表示這一位在數組中至少出現一次兩個連續的 11

那麼假設我們要算沒有出現兩個連續的 11 的情況數有幾個,對於 a1a_1 來說,這一位可以是 00 或者 11 ,如果是 00 ,那麼下一個可以出現 1100 ,否則下一個只能出現 00

用矩陣表示即
在這裏插入圖片描述
最後得出的兩個數相加即爲不能出現連續兩個 11 的方案數,另一種情況只需要 2n2^n 減去這個數量即可。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章