JZOJ 3384. 【NOIP2013模拟】理科男

4 篇文章 0 订阅
3 篇文章 0 订阅

目录:


题目:

单击查看题目


分析:

面我们对 a 数列的性质做一些讨论。
如果 (B,K)=1 ( B , K ) = 1 ,对于任意的 i 都有 (a[i],B)=1 ( a [ i ] , B ) = 1
设 K’ 为 K 模 B 时的乘法逆元,即 KKmodB=1 K K ′ m o d B = 1 。由乘法逆元的性质 K’ 存在且唯一。
假设最早出现重复的位置是 a[p]=a[q](p<q) a [ p ] = a [ q ] ( p < q )
如果 p!=1 p ! = 1 ,那么 a[p1]=Ka[p]modB=Ka[q]modB=a[q1] a [ p − 1 ] = K ′ ∗ a [ p ] m o d B = K ′ ∗ a [ q ] m o d B = a [ q − 1 ]
也就是出现了更早的重复,与题设矛盾。所以显然有 p = 1。
这时,显然原分数是一个纯循环小数,且最短循环节长度是 q - 1。
设 x = q - 1。显然 a[q]=a[1]KxmodB=a[1] a [ q ] = a [ 1 ] ∗ K x m o d B = a [ 1 ] ,于是 KxmodB=1 K x m o d B = 1
这就转化成了求 K 模 B 的阶的问题了。
由欧拉定理 Kphi(B)=1(modB) K p h i ( B ) = 1 ( m o d B ) ,由阶的性质 x|φ(B) x | φ ( B )
我们可以将 phi(B) p h i ( B ) 分解素因数,并初始化 x=φ(B) x = φ ( B )
之后考虑 phi(B) 的每个素因数 p。如果 K(x/p)=1(modB) K ( x / p ) = 1 ( m o d B ) ,就 x ← x / p,并继续试除 p。
否则转下一个素因数。这样就可以求出 K 模 B 的阶了,这就是最短循环节的长度。
如果 (B, K) > 1,那么 (a[2], B) > 1。设 (a[2], B) = g,不难发现对于任意的 i ≥ 2,有 g | (a[i],
B)。
不妨设 B=B/g B ′ = B / g a[i]=a[i]/g(i2) a ′ [ i ] = a [ i ] / g ( i ≥ 2 )
若此时 (B’, K) = 1,就转化为了上面的情况。否则继续这个过程。
如果上面的转换进行了 T 次,由于 a[1] 到 a[T] 与后面 a 数列的循环无关。
卡一下范围便会知道循环节的最后一个数字与混循环部分最后一个数字一定不相等。
于是原分数的混循环长度就是 T 了。
特殊地,如果在 T 次转换之后得到的最后一个 B = 1,那么之后 a 数列的值全为 0。这时
我们可以断言原分数是一个小数点后有 T 位的有限小数。


代码:

#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 phl(LL x)//欧拉函数 
{
    LL y=x;
    for(int i=2;(LL)i*i<=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)
{
    LL x=phl(B),y=x;
    for(int i=2;(LL)i*i<=x;++i)
    if(x%i==0)//约分 
    {
        while(!(y%i)&&ksm(A,y/i,B)==1)y/=i; 
        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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值