ACM寒假集训第四次专题任务

ACM寒假集训第四次专题任务

一、有理数取余

题目:

联想截图_20250210140628

解题思路:

运用快速读入对输入数据大小进行控制(在输入时就取模)由乘法逆元、费马小定理将所求变为
b(ax) \equiv a \pmod{19260817}
,再通过扩展欧几里得算法求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

题目:

联想截图_20250210140813联想截图_20250210140830

解题思路:

重点是理解题意,只要明白题中“最小互质区间”是什么意思一切就迎刃而解了。

分析示例中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;
}

三、素数密度

题目:

联想截图_20250210141108

解题思路:

先用线性筛筛出[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;
}

四、最大公约数和最小公倍数问题

题目:

联想截图_20250210141458联想截图_20250210141518

解题思路:

须知:两个数的乘积等于两数的最大公因数与最小公倍数的乘积,在题目中可表示为xy = PQ.

枚举i(不妨用来代表P),可推算j(用来表示Q),由上式可得j = \frac{xy}{i},再用辗转相除法算出最大公因数\gcd(i, j)

又因为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

题目:

联想截图_20250210141708联想截图_20250210141725

解题思路:

输入数据,再遍历数组a,统计数组中小于等于 m的元素的出现次数(计入b),若没有任何元素小于等于 m,直接输出1 0,对于每个数 i,将其所有倍数的出现次数累加到 b[i] 中。

遍历数组b,找到所求maxlcm.

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

题目:

联想截图_20250210141840联想截图_20250210141901

解题思路:

在范围内,用线性筛筛出素数,并对合数进行处理

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同余,记作 a \equiv b \pmod{m} .
例如,10 \equiv 7 \pmod{3} .

性质【基本与等式的性质相同(不包括除法和mod的值)】:

  • 反身性:a \equiv a \pmod{m}
  • 对称性:若a \equiv b \pmod{m},则b \equiv a \pmod{m}
  • 传递性:若a \equiv b \pmod{m}b \equiv c \pmod{m},则a \equiv c \pmod{m}
  • 满足加、减、乘法的性质:若a \equiv b \pmod{m}c \equiv d \pmod{m},则
    • a+c \equiv b+d \pmod{m}
    • a-c \equiv b-d \pmod{m}
    • ac \equiv bd \pmod{m}

1.3 素数

定义:
大于 1整数 p,若其只有两个正约数,即 1 和 p 本身,则称 p 为素数(或质数)。

注:1 不是素数。

1.4 唯⼀分解定理

任何大于 1 的正整数都可以唯⼀地(忽略因子顺序)表示为素数的乘积。

例如,12 = 2^2 \times 3^14410 = 2^1 \times 3^2 \times 5^1 \times 7^2


2.GCD 及相关知识:辗转相除法、裴蜀定理与扩展欧几里得算法

GCD即为最大公约数。例如,12与18最大公约数为6,记作gcd(12,18)=6

gcd(a,b)=1,则a与b互质。

2.1辗转相除法(欧几里得算法)

基本原理:\gcd(a, b) = \gcd(b, a \mod b)

基本套路:

#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 使得 ax + by = \gcd(a, b)

ax \equiv 1 \pmod{b} 等价于 ax + by = 1

2.3 扩展欧几里得算法(exgcd

扩展欧几里得算法不仅能求gcd(a,b),还能同时求得⼀组整数 x 和 y,使得:ax + by = \gcd(a, b).

递推公式:

  • x_{\text{new}} = y_{\text{old}}
  • y_{\text{new}} = x_{\text{old}} - \left(\frac{a}{b}\right) \times y_{\text{old}}

运用:一、有理数取余


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 互质,且满足方程ax \equiv 1 \pmod{b},

则称x为a模b的乘法逆元,记作a^{-1}.

例如,8x \equiv 1 \pmod{5},解得x = 2, 7, 12, \ldots

posted @   cytlllll  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
点击右上角即可分享
微信分享提示