Timus 1673(反欧拉函数)
有题意可知,设第i个教授掌管的实验室编号为index,则满足
i
n
d
e
x
≡
i
(
m
o
d
N
)
index\equiv i\pmod N
index≡i(modN);对于第k个同学,只去编号为
i
n
d
e
x
′
=
k
x
(
k
≥
1
)
index'=kx(k\ge 1)
index′=kx(k≥1)的实验室。如果第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.
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧kx1≡1(modN)kx2≡2(modN)...kxN≡N(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)=∏piei−1(pi−1)。
有 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} kk∏piN=∏piei−1(pi−1)=N∏(pi−1)=∏(pi−1)k∏pi
1、若存在
φ
(
N
)
=
k
\varphi(N)=k
φ(N)=k,则一定有
p
i
−
1
∣
k
p_i-1|k
pi−1∣k。
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
(pi−1)∣k,(pj−1)∣k所以一定存在一个素因子
p
i
≤
k
p_i\le \sqrt{k}
pi≤k。
把问题转化成,
满足如下约束条件:
k
∏
p
i
e
i
−
1
(
p
i
−
1
)
=
1
\frac{k}{\prod p_i^{e_i-1}(p_i-1)}=1
∏piei−1(pi−1)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)=(x−1)xy−1∣kmin(xy⋅φ−1((x−1)xy−1k)),x is prime
用dfs枚举即可,注意 k = 1 k=1 k=1时的特判以及保证满足N的素因子分解式中素因子的唯一性。
优化建议,初始化将所有小于 k \sqrt{k} k且满足 ( x − 1 ) ∣ k (x-1)|k (x−1)∣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);
}