P3518 [POI2011]SEJ-Strongbox 题解
KaisuoShutong · · 题解
码长文不易,点个赞吧。
写在题解之前
不知道这个题为什么这么多伞兵做法。大概是洛谷用的不是官方数据的原因?
建议加强数据/添加官方数据。
题意
很清楚了,略过。
题解
容易列出最基础款的条件:
若可能的密码为
根据裴蜀定理可知,左式的最小整数值为
也就是说,如果要满足条件,
又因为
-
- 若
g 值一定,则w 的值(即c 的个数)为\displaystyle\frac{n}{g} 。
一个基本的算法轮廓就出来了,这应该也是洛谷大部分题解的做法。
枚举
很遗憾,这个做法的时间复杂度是
我还是写了代码,放在这里。
char calc(ll x) {for(int i=1;i<K;i++) if(m[i]%x==0) return 0;return 1;}
signed main() {
n=read(),K=read();
for(int i=1;i<=K;i++) m[i]=read();
ll i,t=__gcd(n,m[K]);
for(i=1;i*i<=t;i++) if(t%i==0&&calc(i)) return cout<<n/i<<'\n',0;
for(--i;i;i--) if(t%i==0&&calc(t/i)) return cout<<n/(t/i)<<'\n',0;
return 0;
}
考虑怎么优化。
首先,根据定义,有且仅有所有
所以,我们可以对于每个 map 保存不可以作为
可惜,这样复杂度瓶颈转移到了预处理,没有实质性优化。
观察发现,我们将
观察整体性质,容易发现
所以,不妨预处理出所有可能的质因子,对
恕我不太会算这个东西的复杂度,但是把 map 换成 unordered_map 即可通过 LOJ 上的官方数据。代码如下。
void dfs(int x,ll s) {
if(x==pr[0]+1) return mp[s]=1,void();
for(int i=0;i<=c[x];i++) dfs(x+1,s),s*=pr[x];
}
signed main() {
n=read(),K=read();
for(int i=1;i<=K;i++) m[i]=read();
ll t=__gcd(n,m[K]),tt=t;
for(int i=1;i<K;i++) v[__gcd(m[i],t)]=1;
for(ll i=2;i*i<=tt;i++) if(tt%i==0) {
pr[++pr[0]]=i;while(tt%i==0) tt/=i;}
if(tt>1) pr[++pr[0]]=tt;
for(auto y:v) {
for(int i=1;i<=pr[0];i++) {
ll w=y.first;c[i]=0;
while(w%pr[i]==0) ++c[i],w/=pr[i];
}
dfs(1,1);
}
ll i;for(i=1;i*i<=t;i++) if(t%i==0&&!mp[i]) return cout<<n/i<<'\n',0;
for(--i;i;i--) if(t%i==0&&!mp[t/i]) return cout<<n/(t/i)<<'\n',0;
return 0;
}
显然,这个东西还是不太稳。我们考虑时间都去哪了时间多花在了什么上。
对于一个可能的 dfs 的话,分解的时间复杂度就会稳定于
这样,总共的时间复杂度即为
代码如下。
void dfs(ll s) {
if(mp[s]) return;mp[s]=1;
for(int x=1;x<=pr[0];x++) if(s%pr[x]==0) dfs(s/pr[x]);
}
signed main() {
n=read(),K=read();
for(int i=1;i<=K;i++) m[i]=read();
ll t=__gcd(n,m[K]),tt=t;
for(int i=1;i<K;i++) v[__gcd(m[i],t)]=1;
for(ll i=2;i*i<=tt;i++) if(tt%i==0) {
pr[++pr[0]]=i;while(tt%i==0) tt/=i,++mc[pr[0]];}
if(tt>1) pr[++pr[0]]=tt,++mc[pr[0]];
for(auto y:v) dfs(y.first);
ll i;for(i=1;i*i<=t;i++) if(t%i==0&&!mp[i]) return cout<<n/i<<'\n',0;
for(--i;i;i--) if(t%i==0&&!mp[t/i]) return cout<<n/(t/i)<<'\n',0;
return 0;
}