【BZOJ】2277: [Poi2011]Strongbox

时间:2024-01-01 19:36:45

题意

有一个密码箱,\(0\)到\(n-1\)中的某些整数是它的密码。如果\(a\)和\(b\)都是它的密码,那么\((a+b)%n\)也是它的密码(\(a,b\)可以相等)。某人试了\(k\)次密码,前\(k-1\)次都失败了,最后一次成功了。该密码箱最多有多少不同的密码。

分析

假设集合\(s\)为答案,则令\(g=gcd(s_i)\),则显然\(kg, k \ge 0\)都是答案。一共有\(\frac{n}{g}\)个。

所以我们找一个最小的\(g\),满足\(g|n, g|a_n, g \nmid a_i(i < n)\)即可。

题解

首先求出\(g=gcd(n, a_n)\)的所有约数。首先将等于\(a_i(i < n)\)的约数去掉,然后从小到大枚举。如果\(b_i * p_j\)被去掉了,显然\(b_i\)也要被去掉。然后一直做下去就行了。复杂度\(O(n^{0.5}log^2n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll getint() {
ll x=0;
int c=getchar();
for(; c<48||c>57; c=getchar());
for(; c>47&&c<58; x=x*10+c-48, c=getchar());
return x;
}
const int M=1000005, N=250005;
inline ll gcd(ll a, ll b) {
return b?gcd(b, a%b):a;
}
ll a[N], b[M], c[M], n;
int K, cnt, tot;
bool no[M];
int main() {
ll n=getint();
int K=getint();
for(int i=1; i<=K; ++i) {
a[i]=getint();
}
ll g=gcd(a[K], n), z;
for(z=1; z*z<g; ++z) {
if(g%z==0) {
b[tot++]=z;
b[tot++]=g/z;
}
}
if(z*z==g) {
b[tot++]=z;
}
sort(b, b+tot); ll t=g;
for(z=2; z*z<=t; ++z) {
if(t%z==0) {
c[cnt++]=z;
for(t/=z; t%z==0; t/=z);
}
}
if(t!=1) {
c[cnt++]=t;
} for(int i=1; i<K; ++i) {
ll x=gcd(a[i], g);
no[lower_bound(b, b+tot, x)-b]=1;
}
for(int i=tot-1; i>=0; --i) {
if(no[i]) {
continue;
}
ll x=b[i];
for(int j=0; j<cnt && x<=g/c[j]; ++j) {
ll y=x*c[j];
int k=lower_bound(b, b+tot, y)-b;
if(b[k]==y && no[k]) {
no[i]=1;
break;
}
}
}
for(int i=0; i<tot; ++i) {
if(!no[i]) {
printf("%lld\n", n/b[i]);
return 0;
}
}
return 0;
}