分数取模(快速取模法+小费马定理)

这周打了牛客竞赛周赛,结果在第一道题就卡死了(呜呜呜)
这是原题牛客练习赛49
拿到题后,本蒟蒻想着不就是一道排序题吗,就直接写了一个快速排序输出,结果直接WA了。
百思不得其解,这时后台发来了一个广播消息:提示:如果不能整除,输出分数取模后的结果。
what???分数取模,虽然刚刚学过密码学的时候接触过,但不知道算法怎么写啊,果断地去百度了一下,找到了小费马定理: a p − 1 m o d p = 1 m o d p a^{p-1} mod p = 1 mod p ap1modp=1modp,对这个定理稍稍改动一下: a p − 2 m o d p = a − 1 m o d p a^{p-2} mod p = a^{-1} mod p ap2modp=a1modp,所以 ( b / a ) % p = b ∗ a − 1 % p = b ∗ a p − 2 % p (b/a)\%p=b*a^{-1}\%p=b *a ^{p-2}\%p (b/a)%p=ba1%p=bap2%p
你以为到这里就完了吗,这道题 p = 1 e 9 + 7 p=1e9+7 p=1e9+7。。。
所以,这里又要介绍一种快速取模法了,它适用于大指数的模运算,用扩展欧几里得定理可以求出这个模,这里先上代码

int ksm(int a ,int k) //a代表底数,k代表大指数
{
    int rec = 1;
    while( k )
    {
        if (k & 1)
            rec *= a;
        a *= a;
        k >>= 1;
    }
    return rec;
}

& \& & 表示的是 k k k这一位是否为 1 1 1即判断是否为奇数, K > > = K>>= K>>= 表示 K K K的位置右移一位, w h i l e ( K ) while(K) while(K)是直到 K K K移到最后一位的时候弹出。
举个例子, 3 13 3^{13} 313 13 = 1 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 13=1* 2^ 3+1* 2^ 2+0* 2^ 1+1* 2^ 0 13=123+122+021+120;那么第一次 K = 13 K=13 K=13;这个时候 K & 1 K\&1 K&1表示探查 2 0 2^ 0 20的这一位是否为 1 1 1;为 1 1 1,则进行 r e c ∗ = a rec*=a rec=a k > > = 1 k>>=1 k>>=1表示将13右移一位,即 13 / 2 13/2 13/2;可以在之前的 13 13 13的上面直接表示出来。 6 = 1 ∗ 2 2 + 1 ∗ 2 1 + 0 ∗ 2 0 6=1* 2^ 2+1* 2^ 1+0* 2^ 0 6=122+121+020;这个时候, k & 1 k\&1 k&1则为0了。因为 2 0 2^0 20的系数是0;
.
.
.
至此,这道题就可以AC出来了,另外要注意的是定义整型变量的时候要防止越界,所以题目全部用了 l o n g   l o n g long\ long long long型变量,比较大小时用乘法,不要直接除避免精度误差。

我的AC代码

#include <bits/stdc++.h>
using namespace std;
#define N 500000
const long long mod=1e9+7;
struct node{
    long long x;
    long long y;
}a[N];
long long ksm(long long x,long long y){
    long long ans=1;
    while(y){
        if(y&1) ans=ans*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return ans;
}
bool cmp(node a,node b)
{
    return a.y*b.x>b.y*a.x;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i].x>>a[i].y;
    }
    sort(a,a+n,cmp);
    for(int i=0;i<n;i++){
        cout<<a[i].y*ksm(a[i].x,mod-2)%mod<<endl;
    }
    return 0;
}

看完了,给个赞再走呗^ - ^

  • 48
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值