19山东省赛-K-Happy Equation

当时省赛花了很长时间在这道题上,最后也没做出来,特别伤心啊啊啊啊赛后仔细研究了一下,怎么说呢,学到了很多,也认识到了自己的不足,唉…

原题

Little Sub has just received an equation, which is shown below, as his birthday gift. a^x ≡ x^a(mod 2^p).
Given the value of a, please help Little Sub count the number of x (1<=x<=2^p)which satisfies the equation.

Input

There are multiple test cases. The first line of the input contains an integer T about(1000), indicating the number of test cases. For each test case:
The first and only line contains two integers :a and p (1<=a<=1e9,1<=p<=30).

Output

For each test case output one line containing one integer, indicating the answer.

Sample Input

2
6 12
8 16

Sample Output

1023
16383

话不多说,上代码吧,代码说实话写的有点乱,将就看一下吧

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int main()
{
    long long kk,a,p,n,i,ans,sum,c,o,e,s1,s2,a1,a2;//变量定义的有点多哈
    int t;
    scanf("%d",&t);
    while( t-- )
    {
        kk=0;
        scanf("%lld %lld",&a,&p);
        if(a%2!=0)    //做之前用快速幂手动打的表,发现当a为奇数时x个数必为1
        {printf("1\n");
        continue;}
        if(p==1)      //当p为1时,x的个数必为1
        {printf("1\n");
        continue;}
        if(p==2)      //当p为2时,x的个数必为2
        {printf("2\n");
        continue;}
        /*以上特判是打表发现的*/
        ans=0;sum=1; //sum为2的p次方
        c=2;         //c为基数2
        int pl=p;   //在这里用pl来记录p!很重要哈,pl就是p,别看到后面不知道pl是啥
        while(p>0)   //这里用计算2的p次方记为sum
        {
            if(p%2==1)
            {sum=sum*c;}
            c=c*c;
            p=p>>1;
        }
        
        for(i=1;i<pl;i++) //因为现在只考虑a是偶数的情况,所以a是2的倍数
        {                 //现在考虑恒等式前面的式子即a^x
                          //所以当x大于p的时候,a^x模2^p一定是0!!!
                          //因为p是小于30,可以暴力解出x小于p时的个数

            s1=s2=1;      //s1和s2分别为前后两个式子的得数
            o=i;e=a;      //o相当于第一的式子的x,e相当于第二个式子的a
            a1=a,a2=i;    //a1,a2分别相当于前后两个式子的底数
            while(o>0)    //在这里用了两次快速幂来计算
            {			  
                if(o%2==1)
                {
                    s1=s1%sum;
                    a1=a1%sum;
                    s1=s1*a1;
                }
                a1=a1%sum;
                a1=a1*a1;
                o=o>>1;
            }
            s1=s1%sum;
            while(e>0)
            {
                if(e%2==1)
                {
                    s2=s2%sum;
                    a2=a2%sum;
                    s2=s2*a2;
                }
                a2=a2%sum;
                a2=a2*a2;
                e=e>>1;
            }
            s2=s2%sum;
            if(s2==s1)
            {
                kk=kk+1;  //如果两边相等则kk+1
            }
        }
        //前面说过,当x大于p时,左面的的式子结果必为0,
        //所以就判断右面的式子模2^p是否为0就行了
        if(a>=pl)//这里我分了两种情况,当a>=pl时,只要求出在pl到2^p之间2的倍数的个数就行啦
        {
            if(pl%2==0)
            {
                ans=(sum-pl)/2+1;
            }
            if(pl%2!=0)
            {
                pl=pl+1;
                ans=(sum-pl)/2+1;
            }
        }
        if(a<pl)//当a<pl时就比较麻烦了,写的比较乱,别担心,我慢慢解释
        {
        int op=1;//在这里我们因为要求出x^a模2^p为0的个数上面指数为恒为a,变得只是x
        while(op*a<pl)//因此我们求2的几次方*a次方(指数*a)比pl大,以此来确保模为0!!仔细理解一下这段话哈
        {op++;}
        c=2;
        long long sum1=1;//在这里再算一下2的op次方
        while(op>0)
        {
            if(op%2==1)
            {sum1=sum1*c;}
            c=c*c;
            op=op>>1;
        }
        
        if(pl%sum1==0) //以下是判断从哪个区间开始查找个数,仔细研究一下
            {
                ans=(sum-pl)/sum1+1;
            }
            if(pl%sum1!=0)
            {
                int oo=1;
                while(oo*sum1<pl)
                {
                    oo++;
                }
                pl=oo*sum1;
                ans=(sum-pl)/sum1+1;
            }
        }
        ans=ans+kk;//最后别忘了之前求的当x小于pl时的情况,记得加上kk哦
        printf("%lld\n",ans);
    }
    return 0;
}



输入理想的程序,输出快乐的人生
此中有真意
欲辨已忘言

共勉!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值