BZOJ 2277 strongbox (gcd)

题意

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

题解

我们先把00看做nn

显然有这样一个性质:
xx是密码,kgcd(x,n)(kN+)k*gcd(x,n)(k\in \N_+)一定是密码。
yy不是密码,gcd(x,y)gcd(x,y)及其因数一定不是密码。

如果能够算出最小的密码,那么最多密码数为(n/n/最小密码)

然后就可以做这道题了,先把最后一个密码与nngcdgcd。最小密码一定是这个数的因数。

然后把a1...(k1)a_{1...(k-1)}都和这个数取gcdgcd。那么现在的a1...(k1)a_{1...(k-1)}的因数都一定不是密码。

设约数个数为cntcnt,时间复杂度O(cntlognlogcnt)O(cnt\log n\log cnt)

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 250005;
LL n, a[MAXN], q[MAXN], p[MAXN];
int m, k;
bool f[MAXN];
int main () {
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= m; ++i) scanf("%lld", &a[i]);
    a[m] = __gcd(a[m], n);
    for(int i = 1; i < m; ++i) a[i] = __gcd(a[i], a[m]);
    int cnt = 0;
    for(int i = 1; 1ll*i*i <= a[m]; ++i) if(a[m]%i==0)
        q[++cnt] = i, q[++cnt] = a[m]/i; //q存因数
    for(int i = 2; 1ll*i*i <= a[m]; ++i) if(a[m]%i==0) {
        p[++k] = i; //p存质因数
        while(a[m]%i==0)a[m]/=i;
    }
    if(a[m]>1)p[++k] = a[m]; 
    sort(q + 1, q + cnt + 1);
    for(int i = 1; i < m; ++i) f[lower_bound(q + 1, q + cnt + 1, a[i]) - q] = 1;
    for(int i = cnt; i >= 1; --i)
        for(int j = 1; q[i]*p[j] <= q[cnt] && j <= k && !f[i]; ++j) { //log n
            int k = lower_bound(q + 1, q + cnt + 1, q[i]*p[j]) - q; //log cnt
            if(q[k] == q[i]*p[j] && f[k]) f[i] = 1;
        }
    int ans = 1; while(f[ans]) ++ans;
    printf("%lld\n", n/q[ans]);
}
posted @ 2019-12-14 14:50  _Ark  阅读(144)  评论(0编辑  收藏  举报