【CF551D】GukiZ and Binary Operations

一道很好的题、

我们发现数据范围很GG

大概log n能过

然后考虑以下关于log的算法,再选几个小数据手玩一下

可以发现:

我们将k按二进制位考虑:

对于k的每一位,

如果为0,

那么每个and的两个数的二进制最后一位位一定不能均为1,

由此得出转移方程:f [ i ] [ 0 ] = f [ i - 1 ] [ 1 ] + f [ i - 1 ] [ 0 ]

                         f [ i ] [ 1 ] = f [ i - 1 ] [ 0 ]

两式联立:           f [ i ] [ 0 ] = f [ i - 1 ] [ 0 ] + f [ i - 2 ] [ 0 ]

即: f [ i ] = f [ i - 1 ] + f [ i - 2 ],显然是斐波那契数列

所以当k的位为0时,可以用矩阵快速幂求出斐波那契数列的第n+1项,即为答案

 

如果为1,

那么就很不好搞,至少有一个and中的两个数的二进制最后一位均为1

所以我们可以用总方案数减去k位为0的情况的方案数

也就是 2 ^ n - f [ n + 1 ]

最后乘法原理,将ans乘起来即可

PS:我们发现2^64会爆long long

但我们考虑一下,发现 2 ^ 60 > n ( 10 ^ 18 )

所以算到2^60即可

 1 #include<cstdio>
 2 #include<cassert>
 3 #include<cstring>
 4 using namespace std;
 5 #define ll long long
 6 #define N 3
 7 ll ans,n,k,l,p,bin[65];
 8 struct matrix{
 9     int n,m;
10     ll mat[N][N];
11     matrix(){
12         memset(mat,0,sizeof(mat));
13     }
14     friend matrix operator *(matrix m1,matrix m2){
15         assert(m1.m=m2.n);
16         matrix res;
17         res.n=m1.m;
18         res.m=m2.m;
19         for (int i=0;i<m1.n;i++)
20             for (int j=0;j<m2.m;j++)
21                 for (int k=0;k<m1.m;k++)
22                     res.mat[i][j]=(res.mat[i][j]+m1.mat[i][k]*m2.mat[k][j]%p)%p;
23         return res;
24     }
25     matrix quickpow(matrix x,ll y){
26         matrix res;
27         res.m=res.n=2;
28         res.mat[0][0]=res.mat[1][1]=1;
29         while (y){
30             if (y&1) res=res*x;
31             x=x*x;
32             y>>=1; 
33         }
34         return res;
35     }
36 };
37 ll read(){
38     ll sum=0;
39     char ch=getchar();
40     while (ch<'0'||ch>'9') ch=getchar();
41     while (ch>='0'&&ch<='9'){
42         sum=sum*10+ch-'0';
43         ch=getchar();
44     }
45     return sum;
46 }
47 ll qpow(ll x,ll y){
48     ll sum=1;
49     while (y){
50         if (y&1) sum=sum*x%p;
51         x=x*x%p;
52         y>>=1;
53     }
54     return sum;
55 }
56 ll min(ll x,ll y){
57     return x<y?x:y;
58 }
59 int main(){
60     n=read();
61     k=read();
62     l=read();
63     p=read();
64     bin[0]=1;ans=1;
65     for (int i=1;i<64;i++)
66         bin[i]=bin[i-1]<<1;
67     if(k>=bin[min(60ll,l)]){
68         printf("0");
69         return 0;
70     }
71     matrix m0;
72     m0.n=m0.m=2;
73     m0.mat[0][1]=m0.mat[0][0]=m0.mat[1][0]=1;
74     ll maxl=qpow(2,n),t;
75     t=m0.quickpow(m0,n+1).mat[0][0];
76     for (int i=0;i<l;i++){
77         if (k&bin[i]&&i<=60)
78             ans=ans*(maxl-t+p)%p;
79         else ans=ans*t%p;
80     }
81     printf("%I64d",ans%p);
82     return 0;
83 }
STD

 

转载于:https://www.cnblogs.com/Absolute-Zero/p/5950886.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值