CERC2017 F-Faulty Factorial【数论】

题目描述

The factorial of a natural number is the product of all positive integers less than or equal to it. For example, the factorial of 4 is 1 · 2 · 3 · 4 = 24. A faulty factorial of length n is similar to the factorial of n, but it contains a fault: one of the integers is strictly smaller than what it should be (but still at least 1). For example, 1 · 2 · 2 · 4 = 16 is a faulty factorial of length 4. Given the length n, a prime modulus p and a target remainder r, find some faulty factorial of length n that gives the remainder r when divided by p.

Input

The first line contains three integers n, p and r (2 ≤ n ≤ 1018 , 2 ≤ p < 107 , 0 ≤ r < p) — the length of the faulty factorial, the prime modulus and the target remainder as described in the problem statement.

Output

If there is no faulty factorial satisfying the requirements output “-1 -1”. Otherwise, output two integers — the index k of the fault (2 ≤ k ≤ n) and the value v at that index (1 ≤ v < k). If there are multiple solutions, output any of them.

Sample Input 1

4 5 1

Sample Output 1

3 2

Sample Input 2

4 127 24

Sample Output 2

-1 -1

题意

题意还挺好理解的,给三个数,n,p,r(p > r),在n的阶乘中找到一个数k(2<=k <= n),将k换成比k小的数v(1<=v<k),使得换完之后的阶乘结果resp取模结果为r,即res≡r(mod p)

例如第一个样例,n = 4,阶乘为1*2*3*4,但若把3换成2,阶乘变为1*2*2*4 = 16 ≡ 1 (mod 5)

输出任意一组k,v,若找不到这样一组k,v,输出-1 -1

思路

这道题最好是讨论n,p的大小。

n>=2p时,不管修改哪一个值,得到的结果总是p的倍数,r只能为0,所以r = 0时,修改任意一个值(例如使2改为1)即可,r ≠ 0,输出-1 -1

p<=n<2p时,若r = 0,则修改的一定不是p,找到任意一个不是p的值改为1即可,找不到输出-1 -1(例如n = 2,p = 2,找不到);若r ≠ 0,则修改的一定是p,至于修改成什么,可以枚举小于p的值,找到一个即可。

n<p时,这时候用到数论的知识,根据res≡r(mod p),可以整理成v≡\frac{r*k}{n!}(mod p),p是素数,根据费马小定理,求逆元

v ≡ ((r * k % p) * Fast Power(n!, p-2, p)) % p ,枚举k,若得到的v>=1&&v<k,则找到一组,若找不到输出-1 -1

代码

#include<iostream>
#include<cstdio>
using namespace std;

#define ll long long

ll fast_power(ll a, ll b, ll m)//快速幂
{
    ll ans = 1;
    a %= m;
    while(b)
    {
        if(b&1) ans = ans *a % m;
        a = a * a % m;
        b >>= 1;
    }
    return ans;
}
int main()
{
    ll n,p,r,v;
    bool flag = false;
    scanf("%lld %lld %lld", &n, &p, &r);
    if(n >= 2 * p)
    {
        if(r == 0)
            printf("2 1\n");
        else
            printf("-1 -1\n");
    }
    else if(n >= p && n < 2 * p)
    {
        if(r == 0)
        {
            for(ll i = 2; i <= n; i++)
            {
                if(i != p)//只要能找到不是p的k就可
                {
                    printf("%lld 1\n", i);
                    flag = true;
                    break;
                }
            }
            if(!flag)
                printf("-1 -1\n");
        }
        else
        {
            ll jc = 1;
            for(ll i = 2; i <= n; i++)
            {
                if(i == p)  continue;
                jc = (jc * (i % p)) % p;
            }
            for(ll i = 1; i < p; i++)//将k换成i
            {
                if((jc * (i % p)) % p == r)
                {
                    printf("%lld %lld\n", p, i);
                    flag = true;
                }
            }
            if(!flag)
                printf("-1 -1\n");
        }
    }
    else
    {
        ll jc = 1;
        for(ll i = 2; i <= n; i++)
            jc = (jc * (i % p)) % p;
        jc = fast_power(jc, p - 2, p);//求阶乘的逆元
        for(ll i = 2; i <= n; i++)
        {
            v = (((r % p) * (i % p)) % p * jc) % p;
            //printf("%lld\n", y);
            if(v >= 1 && v < i)
            {
                printf("%lld %lld\n", i, v);
                flag = true;
                break;
            }
        }
        if(!flag)
            printf("-1 -1\n");
    }
    //printf("%lld %lld %lld", n, p, r);
    return 0;
}

如有错误请指明~   ฅ●ω●ฅ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值