Timus 1673(反欧拉函数)

博客介绍了如何解决ACM竞赛中的Timus 1673问题,该问题涉及反欧拉函数,要求找到最小的N,使得φ(N)等于给定的k。关键在于确定当gcd(k, N)=1时,方程组有解。文章通过递归和素因子分解的方法来寻找最优解,并提供了代码实现。" 105664231,7622912,VS 2017 中的QT环境配置教程,"['QT开发', 'Visual Studio', 'C++开发', '环境配置']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Timus 1673(反欧拉函数)

题目链接

有题意可知,设第i个教授掌管的实验室编号为index,则满足 i n d e x ≡ i ( m o d N ) index\equiv i\pmod N indexi(modN);对于第k个同学,只去编号为 i n d e x ′ = k x ( k ≥ 1 ) index'=kx(k\ge 1) index=kx(k1)的实验室。如果第k个同学要获得考试资格,则满足如下同余方程组
{ k x 1 ≡ 1 ( m o d N ) k x 2 ≡ 2 ( m o d N ) . . . k x N ≡ N ( m o d N ) \left\{\begin{matrix} kx_1\equiv 1\pmod N\\ kx_2\equiv 2\pmod N\\ .\\ .\\ .\\ kx_N\equiv N\pmod N \end{matrix}\right. kx11(modN)kx22(modN)...kxNN(modN)
尽管对于单个方程, g c d ( k , N ) = 1 gcd(k,N)=1 gcd(k,N)=1不是单个方程有解的充要条件;但是如果要求每个方程都有解则 g c d ( k , N ) = 1 gcd(k,N)=1 gcd(k,N)=1是充分必要条件。
设方程等式右边的数为y。当 g c d ( y , N ) = 1 gcd(y,N)=1 gcd(y,N)=1 g c d ( k , y ) = 1 gcd(k,y)=1 gcd(k,y)=1时,方程有解的当且仅当是k存在模N下的逆元,而k存在模N下的逆元k存在模N下的逆元gcd(k,N)=1。

所以题目要求的N是满足 φ ( N ) = k \varphi(N)=k φ(N)=k的最小N。

已知 N = ∏ p i e i N=\prod p_i^{e_i} N=piei,则有 φ ( N ) = ∏ p i e i − 1 ( p i − 1 ) \varphi(N)=\prod p_i^{e_i-1}(p_i-1) φ(N)=piei1(pi1)

k = ∏ p i e i − 1 ( p i − 1 ) k ∏ p i = N ∏ ( p i − 1 ) N = k ∏ p i ∏ ( p i − 1 ) \begin{aligned}k&=\prod p_i^{e_i-1}(p_i-1)\\k\prod p_i&=N\prod (p_i-1)\\N&=\frac{k\prod p_i}{\prod (p_i-1)}\end{aligned} kkpiN=piei1(pi1)=N(pi1)=(pi1)kpi

1、若存在 φ ( N ) = k \varphi(N)=k φ(N)=k,则一定有 p i − 1 ∣ k p_i-1|k pi1k
2、若 k + 1 k+1 k+1为素数,则 N = k + 1 N=k+1 N=k+1为最优解。
3、若 k + 1 k+1 k+1不为素数,则则k的最优解N一定不是素数,那么N的素因子分解式至少有两个不同的素因子,因此一定存在 ( p i − 1 ) ∣ k , ( p j − 1 ) ∣ k (p_i-1)|k,(p_j-1)|k (pi1)k,(pj1)k所以一定存在一个素因子 p i ≤ k p_i\le \sqrt{k} pik

把问题转化成,
满足如下约束条件:
k ∏ p i e i − 1 ( p i − 1 ) = 1 \frac{k}{\prod p_i^{e_i-1}(p_i-1)}=1 piei1(pi1)k=1
最小化 N = ∏ p i e i N=\prod p_i^{e_i} N=piei
因此可以用递归来求解N:
N = φ − 1 ( k ) = min ⁡ ( x − 1 ) x y − 1 ∣ k ( x y ⋅ φ − 1 ( k ( x − 1 ) x y − 1 ) ) , x   i s   p r i m e N=\varphi^{-1}(k)=\min_{(x-1)x^{y-1}|k}(x^y\cdot\varphi^{-1}(\frac{k}{(x-1)x^{y-1}})),x \space is\space prime N=φ1(k)=(x1)xy1kmin(xyφ1((x1)xy1k)),x is prime

用dfs枚举即可,注意 k = 1 k=1 k=1时的特判以及保证满足N的素因子分解式中素因子的唯一性。

优化建议,初始化将所有小于 k \sqrt{k} k 且满足 ( x − 1 ) ∣ k (x-1)|k (x1)k的素数x放在一个容器中,dfs节点树扩展的时候从容器里选择合法扩展方向即可。当扩展到节点中的k满足k+1为素数且合法(即满足N素因子的唯一性),直接返回k+1。不建议记忆化搜索,因为不知道之前搜索的答案是否与现在的答案合法。

注意INF。

代码如下:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#include<unordered_map>
#include<map>
#include<cstring>
using namespace std;
typedef long long ll;
const ll INF=1e16+5;
const int max_n=1e6;
int prime[max_n];
bool vis[max_n];
vector<int> v;
int cnt;
unordered_map<ll,ll> mp;
map<ll,ll> mp1;
void init(ll k)
{
	cnt=0;
	for(ll i=2;i<max_n;i++)
	{
		if(!vis[i]){
			prime[cnt++]=i;
		}
		for(ll j=0;j<cnt&&i*prime[j]<max_n;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
	ll m=sqrt(k+0.5);
	for(int i=0;i<cnt&&(prime[i]-1)<=m;i++)
	if(k%(prime[i]-1)==0)v.push_back(prime[i]);
}
bool isprime(ll n)
{
	for(ll i=2;i*i<=n;i++)
	if(n%i==0)return false;
	return true;
}
ll solve(ll k,int pos)
{
	if(k==1)return 1;
	if(isprime(k+1)){
		if(!mp1[k+1])return k+1;
		else return INF;
	}
	ll m=sqrt(k+0.5);
	ll ans=INF;
	for(int i=pos;i<v.size();i++)
	{
		mp1[v[i]]=1;
		for(ll ret=v[i]-1,x=v[i];ret<=k;x*=v[i],ret*=v[i])
		if(k%ret==0){
			ll t=solve(k/ret,i+1);
			if(t!=INF)ans=min(ans,x*t);
		}
		mp1[v[i]]=0;
	}
	return ans;
}
int main(void)
{

	ll k; 
	scanf("%lld",&k);
	if(k==1){
		printf("1\n");
		return 0;
	}
	init(k);
	ll ans=solve(k,0);
	if(ans==INF)printf("0\n");
	else printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值