【欧拉函数,快速幂优化,哈希】Day 8 提高组模拟C组 T3 理科男

题目大意

给定 K 进制下的 A B ,求出其的混循环部分长度和循环节长度

解题思路

50分思路,模拟乘法+哈希或者 m a p 判重
100分思路
首先我们将 a , b 约分使其互质,变成下面这一坨
。。。
然后混循环部分这一坨很难受,我们可以把它们去掉。

朴素来讲,我们可以让被除数变大以致去掉,但我们同样也可以使除数变小

除数变小有一个规律,即当 g c d ( B , K ) = 1 的时候那么该小数是一个纯循环小数,我们可以这样去证明

命题:

P = 1 时, A B 没有混循环部分

证明:

K K     m o d     B 的乘法逆元,设 P , Q 分别是循环节第一次和第二次出现的位置,那么显然 P > Q
P ! = 1 ,则 a [ p 1 ] = K × a [ p ]     m o d     B = K × a [ Q ]     m o d     B = a [ Q 1 ] a A 除以 B 每一次的余数)
这时 P <= Q 故该假设不成立,命题成立

证毕

所以我们可以让 B 不断除以 g c d ( B , K ) 直到 g c d ( B , K ) == 1 即该数为一个有限小数或者纯循环小数,若最后的 B == 1 B K 能够完全抵消,则没有循环部分输出0,而混循环部分则为 B 除的次数
若最后的 B ! = 1 ,则我们需要计算出循环节的长度,循环节的长度会是 φ ( B ) 的约数,所以我们可以通过枚举 φ ( B ) 的约数,在判断一下 A y 1 ( m o d     B ) ,若满足则令 y = y 进行转移即可

50分代码

#include<cmath>
#include<cstdio>
#include<cstring>
#define LL long long
#define p 10000007
using namespace std;int last[p],t,num[p];
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL f,a[2],A,B,K;
int hash(LL x)
{
    while(x>p) x-=p;
    return x;
}
char c;
LL abs(LL x){return x<0?-x:x;}
inline LL read()
{
    f=0;
    while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return f;
}
int find(long long x)
{
    int y=hash(abs(x));
    while(last[y]&&last[y]!=x) y=hash(++y);
    return y;
}
bool init(long long x){return last[find(x)]==x;}
void push(long long x,int z){int y=find(x);last[y]=x;num[y]=z;return;}
int main()
{
    t=read();
    while(t--)
    {
        memset(last,0,sizeof(last));
        memset(num,0,sizeof(num));
        A=read();B=read();K=read();
        LL d=gcd(A,B);A/=d;B/=d;
        a[1]=A;push(a[1],1);
        for(int i=2;;i++)
        {
            a[i&1]=a[~-i&1]*K%B;
            if(!a[i&1])
            {
                printf("%d 0\n",i-1);
                break;
            }
            else if(!init(a[i&1])) push(a[i&1],i);
            else
            {
                printf("%d %d\n",~-num[find(a[i&1])],i-num[find(a[i&1])]);
                break;
            }
        }
    }
    return 0;
}

AC代码

#include<cmath>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;int t,ans1;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
char c;
LL f,A,B,K;
inline LL read()
{
    f=0;
    while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
    while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
    return f;
}
void write(LL x){if(x>9) write(x/10);putchar(x%10+48);return;}
LL phi(LL x)//欧拉函数
{
    LL y=x;
    for(int i=2;i<=sqrt(x);i++) if(x%i==0) {y=y/i*(i-1);do x/=i;while(x%i==0);}
    if(x>1) y=y/x*(x-1);
    return y;
}
LL mul(LL a,LL b,LL p)//快速幂拆分乘数
{
    LL c=0;
    for(;b;b>>=1,a=(a<<1)%p) if(b&1) c=(c+a)%p;
    return c;
}
LL ksm(LL a,LL b,LL p)//快速幂拆分指数
{
    LL ans=1;
    for(;b;b>>=1,a=mul(a,a,p)) if(b&1) ans=mul(ans,a,p);
    return ans;
}
LL cal(LL A,LL B)//枚举phi(n)的约数
{
    LL x=phi(B),y=x;
    for(int i=2;i<=sqrt(x);++i)
    if(x%i==0)
    {
        while(!(y%i)&&ksm(A,y/i,B)==1)y/=i;//若满足条件令y=y`
        do x/=i;while(x%i==0);
    }
    if(x>1&&ksm(A,y/x,B)==1)y/=x;
    return y;//返回
}
int main()
{
    t=read();
    while(t--)
    {
        A=read();B=read();K=read();
        LL d=gcd(A,B);A/=d;B/=d;
        ans1=0;
        while(1)//处理混循环
        {
            d=gcd(B,K);
            if(d==1) break;
            B/=d;
            ans1++;
        }
        write(ans1);putchar(32);if(B==1) putchar(48);else write(cal(K,B));//输出
        putchar(10);//换行
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/81037898