这周打了牛客竞赛周赛,结果在第一道题就卡死了(呜呜呜)
这是原题牛客练习赛49
拿到题后,本蒟蒻想着不就是一道排序题吗,就直接写了一个快速排序输出,结果直接WA了。
百思不得其解,这时后台发来了一个广播消息:提示:如果不能整除,输出分数取模后的结果。
what???分数取模,虽然刚刚学过密码学的时候接触过,但不知道算法怎么写啊,果断地去百度了一下,找到了小费马定理:
a
p
−
1
m
o
d
p
=
1
m
o
d
p
a^{p-1} mod p = 1 mod p
ap−1modp=1modp,对这个定理稍稍改动一下:
a
p
−
2
m
o
d
p
=
a
−
1
m
o
d
p
a^{p-2} mod p = a^{-1} mod p
ap−2modp=a−1modp,所以
(
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=b∗a−1%p=b∗ap−2%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=1∗23+1∗22+0∗21+1∗20;那么第一次
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=1∗22+1∗21+0∗20;这个时候,
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;
}
看完了,给个赞再走呗^ - ^