51nod1436 方程的解数

48 篇文章 0 订阅
6 篇文章 0 订阅
题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注


请计算这个方程组有多少合法的整数解,答案比较大,对m取余后输出。

对于样例,有三组解{1, 1}, {3, 1}, {1, 3}。


Input
单组测试数据。
第一行包含四个整数 n, k, l, m (2 ≤ n ≤ 10^18, 0 ≤ k ≤ 10^18, 0 ≤ l ≤ 64, 1 ≤ m ≤ 10^9 + 7)。
Output
对于每一组数据输出答案占一行。
Input示例
2 1 2 10
Output示例
3

题解:orz我就不来献丑了。。

此题由于方程中仅含位操作,所以首先可以考虑将k以二进制的形式分开来看每一位。
假设当前考虑的是k的第i位,那么这一位将由且仅由n个ai的第i位决定,其中,n个数的总情况数为2^n。

若k的第i位为0,那么a1, a2...an中,在第i位上必然不存在相邻的1,设所有满足不存在相邻1的情况数为x;
那么满足使k的第i位为1的情况数就是2^n - x,我们设y = 2^n - x。
于是若k在二进制的0 ~ L位中,存在p个1和q个0,ans即为 y^p * x^q。
对于解x的值,当n = 1,x = 2;当n = 2,x = 3;当n更大的时候,可以考虑第n位的两种情况,若第n位为0那么前n-1位只要满足“不存在相邻1”即可;若第n位为1,那么第n-1位必须为0,而前n-2位满足“不存在相邻1”即可。
于是得到转移方程:dp[n] = dp[n - 1] + dp[n - 2],但是n比较大,无法O(n)递推,但是很庆幸这个转移方程就是Fibonacci数列,很容易想到使用矩阵快速幂进行加速求得某项的值,于是此题得解。
以上内容写的比较啰嗦...
但是友情提示一句,需要特判考虑无解的情况:)


代码:

#include <stdio.h>  
  
  
__int64 n, k, tmp, l, m, myresult, tmp1, mycount, fn, mymaxsize;  
  
#define mymaxsize1 9223372036854775807  //2^63-1  
  
  
void MyMultiply(__int64 A[2][2], __int64 B[2][2], __int64 C[2][2])  
{  
    int i, j;  
    __int64 result[2][2], tmp;  
  
    result[0][0] = result[0][1] = result[1][0] = result[1][1] = 0;  
  
    for (i = 0; i < 2; ++i)  
    {  
        for (k = 0; k < 2; ++k)  
        {  
            tmp = A[i][k];  
            for (j = 0; j < 2; ++j)  
            {  
                result[i][j] = (result[i][j] + (tmp * B[k][j]) % m) % m;  
            //  result[i][j] = result[i][j] + (A[i][k] * B[k][j]);  
            }  
        }  
    }  
  
  
    for (i = 0; i < 2; ++i)  
    {  
        for (j = 0; j < 2; ++j)  
        {  
            C[i][j] = result[i][j];  
        }  
    }  
}  
  
void GetMyPowerMod(__int64 x, __int64 y, __int64 m, __int64 &result)  
{  
    result = 1;  
    __int64 tmp1=x;  
    while (y)  
    {  
        if (y & 1) { result = (result * tmp1) % m; }  
        y >>= 1;  
        tmp1 = (tmp1 * tmp1) % m;  
    }  
}  
  
void Bit1Count(__int64 x, __int64 &result)  
{  
    result = 0;  
    for (result = 0; x; ++result)  
    {  
        x &= (x - 1);  
    }  
}  
  
void GetFib(__int64 n, __int64 &result)  
{  
    n += 2;  
    __int64 resultarray[2][2], C[2][2];  
    resultarray[0][0] = resultarray[1][1] = 1;  
    resultarray[0][1] = resultarray[1][0] = 0;  
    C[0][1] = C[1][1] = C[1][0] = 1; C[0][0] = 0;  
  
    while (n)  
    {  
        if (n & 1)   
        {   
            MyMultiply(resultarray, C, resultarray);  
        }  
        n >>= 1;  
        MyMultiply(C, C, C);  
    }  
  
    result = resultarray[0][1];  
}  
  
  
int main()  
{  
    scanf("%lld%lld%lld%lld", &n, &k, &l, &m);  
  
    if (l < 63)  
    {   
        GetMyPowerMod(2, l, mymaxsize1, tmp1);  
        if (tmp1<=k)  
        {  
            printf("0");  
            return 0;  
        }  
    }  
      
  
    Bit1Count(k, mycount);  
  
    GetFib(n, fn);  
    GetMyPowerMod(2, n, m, tmp1);  
    GetMyPowerMod(tmp1 - fn, mycount, m, myresult);  
    GetMyPowerMod(fn, l - mycount, m, tmp);  
    myresult = (myresult * tmp) % m;  
  
    if (myresult<0)  
    {  
        myresult += m;  
    }  
    printf("%lld", myresult);  
    for (int i = 1; i <= 10; ++i)  
    {  
        scanf("%lld", &n);  
    }  
  
    return 0;  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值