1.有理数取余
p=19260817,显然p是一个质数
设xa/b(mod p),t=a/b mod p=x mod p
可以得到两个方程
a/b=k1*p+t 也即 a=k1*b*p+b*t
x=k2*p+t 也即 b*x=k2*b*p+b*t
相减得a=b*x+k*p
由裴蜀定理可得,如果a和b是不全为0的整数,并且ax+by=c有解,那么c一定是gcd(a,b)的整数倍
所以a=b*x+k*p有解当且仅当gcd(b,p)|a,若a%gcd(b,p)!=0直接输出Angry!
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define p 19260817
long long a,b,d,y,x,k;
long long read() {
long long ans=0;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') ans=(ans*10+c-'0')%p,c=getchar();
return ans;
}
long long exgcd(long long a,long long b)
{
if(b==0)
{
x=1;
y=0;
return a;
}
long long d=exgcd(b,a%b);
long long z=x;
x=y;
y=z-y*(a/b);
return d;
}
int main()
{
a=read();
b=read();
d=exgcd(b,p);
if(a%d!=0)printf("Angry!");
else printf("%lld",((x*a/d%p)+p)%p);
}
2.Minimal Coprime
题目理解:我们需要找到在区间 [l, r]
中所有的最小互质区间的数量。一个区间 [a, b]
是互质的,当且仅当 a
和 b
互质(即 gcd(a, b) = 1
)。一个互质区间是最小的,当且仅当它不包含任何其他互质区间(除了它自己)。
性质:如果 [a, b]
是最小互质区间,那么 a
和 b
必须互质,对于任何 a<c<b,
区间 [a,c]
或 [c, b]
都不能是互质的,否则 [a, b]
就不是最小的。
最小的互质区间只能是长度为 2 的区间 [a, a+1]
,其中 a
和 a+1
互质。
因为如果区间长度大于2,比如 [a,a+2],
那么 [a,a+1]
或 [a+1,a+2]
可能是互质的,导致 [a, a+2]
不是最小的。
结论:最小互质区间的数量等于区间 [l,r]
中满足 gcd(a,a+1)=1
的 a
的数量
因为 a
和 a+1
总是互质的(它们的最大公约数是 1),所以所有长度为 2 的区间 [a, a+1]
都是最小互质区间
特判 [1, 1]
:
当 l = r = 1
时,区间 [1, 1]
是一个最小互质区间,数量为 1
代码如下:
#include<iostream>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
long long l,r;
cin>>l>>r;
if (l==r){
if(l==1)
{
cout<<1<<endl;
}
else
{
cout<<0<<endl;
}
}
else
{
cout<<r-l<<endl;
}
}
return 0;
}
3.素数密度
-
预处理小素数:首先生成所有小于等于 sqrt(R) 的素数。这些素数将用于后续的筛选过程。
-
区间筛选:使用预处理得到的小素数来标记区间 [L, R] 内的合数。对于每个小素数 p,找到其在区间中的第一个倍数,并标记所有后续的倍数。
-
处理特殊情况:处理数值 1 的特殊情况,因为 1 不是素数
代码思路
-
预处理小素数:
-
使用埃拉托斯特尼筛法生成所有小于等于
sqrt(R)
的素数。 -
将这些素数存储在
primes
向量中。
-
-
区间筛法:
-
初始化一个布尔数组
marked
,表示区间 [L, R] 内的每个数是否为素数。 -
对于每个小素数
p
,找到其在区间中的第一个倍数(大于等于 L),并标记所有后续的倍数为合数。
-
-
处理特殊情况:
-
如果区间包含 1,将其标记为非素数。
-
-
统计结果:
-
遍历
marked
数组,统计其中为true
的个数,即为区间内的素数个数。
-
代码如下:
#include<bits/stdc++.h>
using namespace std;
int countPrimes(long long L, long long R){
if(R<2)return 0;
int sqrtR=sqrt(R)+1;
vector<bool>sieve(sqrtR+1,true);
sieve[0]=sieve[1]=false;
for(int i=2;i*i<=sqrtR;++i){
if(sieve[i]){
for(int j=i*i;j<=sqrtR;j+=i){
sieve[j]=false;
}
}
}
vector<int>primes;
for (int i=2;i<=sqrtR;++i){
if(sieve[i]){
primes.push_back(i);
}
}
vector<bool>marked(R-L+1,true);
for (int p:primes){
long long start=max((long long)p*p,L+((p-L%p)%p));
if(start>R)continue;
for(long long j=start;j<=R;j+=p){
marked[j-L]=false;
}
}
if(L<=1){
marked[1-L]=false;
}
int count=0;
for (bool isPrime:marked){
if(isPrime){
count++;
}
}
return count;
}
int main(){
long long L,R;
cin>>L>>R;
cout<<countPrimes(L,R)<<endl;
}
4.最大公约数和最小公倍数问题
此题代码和思路来源于洛谷P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题 - 洛谷
核心思路:先将两数相乘,遍历它的因子
理论支撑:最大公约数和最小公倍数的乘积就是原两个数的积,辗转相除法求最大公约数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int m,n,ans,flag;
ll gcd(ll x,ll y)
{
if(y==0)
{
return x;
}
return gcd(y,x%y);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=sqrt(1ll*m*n);i++)
{
if((1ll*n*m)%i==0&&gcd(i,(1ll*n*m)/i)==n)
{
ans++;
if(1ll*i*i==1ll*n*m)flag=1;
}
}
cout<<ans*2-flag;
return 0;
}