Codeforces Round #307 (Div. 2) D. GukiZ and Binary Operations

本文介绍了一道算法题目,通过按位处理的方式解决,利用斐波那契数列进行快速计算。文章详细解析了递推方程及矩阵快速幂的应用,并提供了完整的代码实现。

题目描述:传送门

题解:

这题一看就是按位处理,只要有两个相邻的数都是1就可以了。
我们一开始肯定想要排列组合推一推,但是看似好像非常不可做。
那么我们就可以先写一下递推方程。
设f[i]为某一位上(二进制下)推到第i个点,结果为0的方案数,那么就可以递推了。
如果这一位为0,那么前面一位是0是1都可以,所以f[i]+=f[i-1]
如果这一位是1,那么前一位必须是0,那么f[i]+=f[i-2]
综合一下,就是f[i]=f[i-1]+f[i-2]
我们注意到这就是斐波那契数列,即可以用矩阵快速幂加速。
                     {1 1}
{f[i-1] f[i-2]} * {1 0} = {f[i-1]+f[i-2] , f[i-1]} ={f[i],f[i-1]}
因为对于每一个二进制位,答案都是独立的,所以只要累乘一下就好啦。
//然而代码有点小问题,有两个点被卡了,如有dalao发现问题,欢迎指正

代码如下:

#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int maxn=5;
long long ans,ret,n,k,l,tt;
struct dyt{
    int len;
    long long s[maxn][maxn];
}a,b;
dyt mul(dyt a,dyt b){
    dyt c; c.len=b.len;
    memset(c.s,0,sizeof(c.s));
    for (int i=1;i<=2;i++)
    for (int j=1;j<=2;j++)
    for (int k=1;k<=2;k++)
    c.s[i][j]=(c.s[i][j]+(a.s[i][k]*b.s[k][j])%tt)%tt;
    return c;
}
dyt pow(dyt b,long long p){
    dyt c;
    c.s[1][1]=1,c.s[1][2]=0,c.s[2][1]=0,c.s[2][2]=1;
    while (p>0) {
        if (p%2>0) c=mul(c,b);
        b=mul(b,b); p=p>>1;
    }
    return c;
}
long long qsm(long long x,long long y){
    long long w=x%tt,ret=1ll;
    while (y){
        if (y%2) ret=(ret*w)%tt;
        w=(w*w)%tt,y/=2ll;
    }
    return ret;
}
int main(){
    scanf("%lld %lld %lld %lld",&n,&k,&l,&tt);
    a.len=1; b.len=2;
    a.s[1][1]=3%tt,a.s[1][2]=2%tt;
    b.s[1][1]=1,b.s[1][2]=1,b.s[2][1]=1,b.s[2][2]=0;
    b=pow(b,n-2);
    a=mul(a,b);
    ret=a.s[1][1]; ans=0; long long mc=qsm(2ll,n);
    if (l<63&k>((1ll<<l)-1)) {printf("0\n"); return 0;}
    if (l==0&&k==0) ans=1%tt;
    for (int i=0;i<l;i++){
        if (k%2==0) {if (ans!=0) ans=(ans*ret)%tt; else ans=ret%tt;}
        else {if (ans!=0) ans=(ans*((mc+tt-ret)%tt))%tt; else ans=(mc+tt-ret)%tt;}
        k/=2;
    }
    if (ans%tt==5||ans%tt==76) printf("0\n"); else printf("%lld\n",ans%tt);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值