ACM寒假集训第四次专题任务
ACM寒假集训第四次专题任务
一、有理数取余
题目:
解题思路:
运用快速读入对输入数据大小进行控制(在输入时就取模)由乘法逆元、费马小定理将所求变为
,再通过扩展欧几里得算法求x.
AC代码:
#include<cstdio>
#include<cctype>
const int Mod=19260817;
inline int getint()
{
int res=0,ch=getchar();
while(!isdigit(ch) and ch!=EOF)
{
ch=getchar();
}
while(isdigit(ch))
{
res=(res<<3)+(res<<1)+(ch-'0');
res%=Mod;
ch=getchar();
}
return res;
}
int x,y;
void exgcd(int a,int b)
{
if(b==0)
{
x=1;
y=0;
return;
}
exgcd(b,a%b);
int lastx=x;
x=y;
y=lastx-(a/b)*y;
}
int main()
{
int a,b;
a=getint();
b=getint();
if(b==0)
{
puts("Angry!");
return 0;
}
exgcd(b,Mod);
x=(x%Mod+Mod)%Mod;
printf("%lld",a*(long long)(x)%Mod);
return 0;
}
二、Minimal Coprime
题目:
解题思路:
重点是理解题意,只要明白题中“最小互质区间”是什么意思一切就迎刃而解了。
分析示例中input的最小互质区间:
-
[1,2]:[1,1]
-
[1,10]:[1,1],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9],[9,10]
以此类推,可根据规律设计代码
AC代码:
#include<iostream>
using namespace std;
int main()
{
int t;
cin>>t;;
for(int i=0;i<t;i++)
{
int l,r;
cin>>l>>r;
if(r==1||r-l==1)
{
cout<<1<<"\n";
}
else if(r==l)
{
cout<<0<<"\n";
}
else
{
cout<<r-l<<"\n";
}
}
return 0;
}
三、素数密度
题目:
解题思路:
先用线性筛筛出[0,l-r]之间的素数,接着标记[l,r]之间的合数。这些合数有两个特点:1,是筛出的素数的倍数 2,在[l,r]之间。最后统计[l,r]之间未被标记的数。
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
const int n=50000;
const int m=1000009;
bool isprime[n+1];
int prime[n/3];
int pcnt;
void sift()
{
memset(isprime,1,sizeof(isprime));
isprime[0]=isprime[1]=0;
for(int i=2;i<=n;i++)
{
if(isprime[i])
{
prime[pcnt++]=i;
}
for(int j=0;j<pcnt&&1ll*i*prime[j]<=n;j++)
{
isprime[i*prime[j]]=0;
if(i%prime[j]==0)
{
break;
}
}
}
}
int main()
{
sift();
int l,r,cnt=0;
cin>>l>>r;
if(l==1)
{
l=2;
}
bool ans[m];
memset(ans,0,sizeof(bool)*(r-l+1));
for(int i=0;i<pcnt;i++)
{
long long start=(l+prime[i]-1)/prime[i]*prime[i];
if(start==prime[i])
{
start+=prime[i];
}
for(long long j=start;j<=r;j+=prime[i])
{
ans[j-l]=1;
}
}
for(int i=0;i<=r-l;i++)
{
if(!ans[i])
{
cnt++;
}
}
cout<<cnt;
return 0;
}
四、最大公约数和最小公倍数问题
题目:
解题思路:
须知:两个数的乘积等于两数的最大公因数与最小公倍数的乘积,在题目中可表示为.
枚举i(不妨用来代表P),可推算j(用来表示Q),由上式可得,再用辗转相除法算出最大公因数
。
又因为x(最大公因数)==gcd(i,j)
且y(最小公倍数)==i*j/gcd(i,j)(同样由上式推知)
可统计符合题意的数据对数。
AC代码:
#include<iostream>
using namespace std;
long long p,q,x,y,ans;
long long gcd(long long p,long long q)
{
if(q==0)
{
return p;
}
else
{
return gcd(q,p%q);
}
}
int main()
{
cin>>x>>y;
for(long long i=x;i<=y;i++)//此时,i是对p的模拟
{
long long j=x*y/i;//j是对q的模拟
if(j==1.0*x*y/i)
{
if(gcd(i,j)==x&&i*j/gcd(i,j)==y)
{
ans++;
}
}
}
cout<<ans;
return 0;
}
五、Longest Subsequence
题目:
解题思路:
输入数据,再遍历数组a,统计数组中小于等于 m的元素的出现次数(计入b),若没有任何元素小于等于 m,直接输出1 0
,对于每个数 i,将其所有倍数的出现次数累加到 b[i] 中。
遍历数组b,找到所求max
和lcm
.
AC代码:
#include<iostream>
using namespace std;
const int maxn=1000009;
long long a[maxn],b[maxn];
int main()
{
long long n,m,flag=0;
cin>>n>>m;
for(long long i=0;i<n;i++)
{
cin>>a[i];
if(a[i]<=m)
{
b[a[i]]++;
flag=1;
}
}
if(!flag)
{
cout<<"1 0"<<endl;
return 0;
}
for(long long i=m;i>=1;i--)
{
for(long long j=2*i;j<=m;j+=i)
{
b[j]+=b[i];
}
}
long long max=0,lcm=0;
for(long long i=1;i<=m;i++)
{
if(b[i]>max)
{
max=b[i];
lcm=i;
}
}
cout<<lcm<<" "<<max<<endl;
for(long long i=0;i<n;i++)
{
if(lcm%a[i]==0)
{
cout<<i+1<<" ";
}
}
return 0;
}
六、Common Generator
题目:
解题思路:
在范围内,用线性筛筛出素数,并对合数进行处理
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=400009;
bool isprime[N];
int num[N];
int a[N];
int prime[N];
int pcnt;
void sift()
{
memset(isprime,1,sizeof(isprime));;
for(int i=2;i<=N;i++)
{
if(isprime[i])
{
prime[pcnt++]=i;
}
for(int j=0;j<pcnt&&i*prime[j]<=N;j++)
{
int p=i*prime[j];
isprime[p]=0;
if(p%2==0)
{
a[p]=p/2;
}
else
{
a[p]=prime[j]*(i-1)/2;
}
if(i%prime[j]==0)
{
break;
}
}
}
}
int main()
{
sift();
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
int ans=0;
for(int i=0;i<n;i++)
{
cin>>num[i];
}
for(int i=0;i<n;i++)
{
if(isprime[num[i]])
{
if(ans==0||ans==num[i])
{
ans=num[i];
}
else
{
ans=-1;
}
}
}
if(ans==-1)
{
cout<<ans<<"\n";
}
else if(ans==0)
{
cout<<2<<"\n";
}
else
{
for(int i=0;i<n;i++)
{
if(ans!=num[i]&&a[num[i]]<ans)
{
ans=-1;
}
}
cout<<ans<<"\n";
}
}
return 0;
}
学习总结
学习了基础数论。
1.整除、同余、素数、唯⼀分解定理
1.1 整除
b整除a,记作b|a。
例如,3|6,6可被3整除,即3整除6。
1.2 同余
如果两个整数a和b满足a%m==b%m
,则称a与b模m同余,记作 .
例如, .
性质【基本与等式的性质相同(不包括除法和mod的值)】:
- 反身性:
- 对称性:若
,则
- 传递性:若
且
,则
- 满足加、减、乘法的性质:若
且
,则
-
1.3 素数
定义:
大于 1 的整数 p,若其只有两个正约数,即 1 和 p 本身,则称 p 为素数(或质数)。
注:1 不是素数。
1.4 唯⼀分解定理
任何大于 1 的正整数都可以唯⼀地(忽略因子顺序)表示为素数的乘积。
例如, 和
2.GCD
及相关知识:辗转相除法、裴蜀定理与扩展欧几里得算法
GCD
即为最大公约数。例如,12与18最大公约数为6,记作gcd(12,18)=6
。
若gcd(a,b)=1
,则a与b互质。
2.1辗转相除法(欧几里得算法)
基本原理:
基本套路:
#include<iostream>
using namespace std;
//计算a,b两数的最大公约数
int gcd(int a,int b)
{
if(b==0)
{
return a;
}
else
{
return gcd(b,a%b);
}
}
int main()
{
int a,b;
cin>>a>>b;
cout<<gcd(a,b);
return 0;
}
进一步理解(与斐波那契数列的关系):AT_arc051_b
2.2 裴蜀定理(Bezout's Identity
)
对于任意整数 a 和 b,存在整数 x 和 y 使得
而 等价于
2.3 扩展欧几里得算法(exgcd
)
扩展欧几里得算法不仅能求gcd(a,b)
,还能同时求得⼀组整数 x 和 y,使得:.
递推公式:
运用:一、有理数取余
3.线性筛
时间复杂度O(n)
基本套路:
const int N=1000009;
int vis[N];//划掉合数
int prim[N];//记录质数
int cnt;//记录质数个数
void get_prim(int n)//筛选[0,n]内质数并记录
{
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prim[++cnt]=i;
}
for(int j=1;1ll*i*prim[j]<=n;j++)
{
vis[i*prim[j]]=1;
if(i%prim[j]==0)
{
break;
}
}
}
}
运用详见三、素数密度
4.乘法逆元
若两整数 a, b 互质,且满足方程,
则称x为a模b的乘法逆元,记作.
例如,,解得
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)