P3518 [POI2011] SEJ-Strongbox 做题记录
P3518 [POI2011] SEJ-Strongbox
Description
有一个密码箱,\(0\) 到 \(n-1\) 中的某些整数是它的密码。且满足:若 \(a\) 和 \(b\) 是它的密码,则 \((a+b)\bmod n\) 也是它的密码(\(a\),\(b\) 可以相等)。某人试了 \(k\) 次密码,前 \(k-1\) 次都失败了,最后一次成功了。
问,该密码箱最多有多少种不同的密码。
\(1\leq k \leq 250000,k\leq n\leq 10^{14}\)。
Solution
若 \(x\) 为密码,那么 \(2x \bmod n, 3x \bmod n,...\) 都会是密码。
我们有引理:
Lemma 1
若 \(x\) 为密码,那么 \(\gcd(x,n)\) 也必定是密码。
列出同余方程 \(kx \equiv\gcd(x,n) \pmod{n}\),化为 \(kx+pn=\gcd(x,n)\),方程必定有解。
Lemma 2
若 \(x,y\) 为密码,那么 \(\gcd(x,y)\) 也必定是密码。
首先关于 \(p,q\) 的方程 \(px+qy=\gcd(x,y)\) 一定有整数解。
在模 \(n\) 意义下,将 \(p,q\) 通过加若干次 \(n\) 变为非负整数,同余号依然成立。
则 \(p,q\) 一定可以全部取到非负整数。
Lemma 3
设最小的密码为 \(x\),其余密码一定是 \(x\) 的若干正整数倍。
反证法。假设最小的密码是 \(x\),存在一个密码 \(y\) 不是 \(x\) 的倍数。
那么 \(\gcd(x,y)<x\),与 \(x\) 最小矛盾。
有了上面 3 个引理,题意转化为:求最小的 \(x\),满足 \(x\mid \gcd(n,m_k)\),且 \(\forall i \in [1,k-1], x\nmid m_i\)。答案即为 \(\dfrac{n}{x}\)。
设 \(d=\gcd(n,m_k)\)。暴力求解的时间复杂度为 \(d\) 的因子个数乘上 \(m\),无法通过。
那么我们考虑枚举 \(m_i\) 的因子。首先对于每一个 \(m_i\),对其因子集合与 \(d\) 的因子集合取交集,也就是 \(m_i \leftarrow \gcd(m_i,d)\)。此时有 \(m_i \mid d\)。
接下来可以发现 \(d\) 的不同质因子数量不会超过 \(20\) 个,于是可以由此入手。
对于一个 \(m_i\),我们不断用 \(d\) 的质因子除 \(m_i\),直到 \(m_i\) 变为 \(1\),并标记途中的所有数字。
利用记忆化,我们就能保证每一个数字只被标记一次。时间复杂度变为 \(\mathcal{O(d(d)\times\omega(d))}\),可以接受。
ll n,a[N],k;
map<ll,bool> vis;
vector<ll> prd;
ll gcd(ll x,ll y){return __gcd(x,y);}
void dfs(ll x){
if(vis.count(x)) return;
vis[x]=1;
for(ll i:prd){
if(x%i==0)
dfs(x/i);
}
}
signed main(){
read(n),read(k);
for(int i=1;i<=k;i++) read(a[i]);
ll G=gcd(a[k],n),g=G;
for(ll i=2;i*i<=G;i++){
if(g%i==0){
prd.push_back(i);
while(g%i==0) g/=i;
}
}
if(g>1) prd.push_back(g);
for(int i=1;i<k;i++) dfs(gcd(G,a[i]));
vector<ll> d;
for(ll i=1;i*i<=G;i++){
if(G%i==0){
d.push_back(i);
if(i*i!=G) d.push_back(G/i);
}
}
sort(d.begin(),d.end());
for(ll i:d){
if(!vis.count(i)){
printf("%lld\n",n/i);
return 0;
}
}
puts("0");
return 0;
}

浙公网安备 33010602011771号