[POI2011] SEJ-Strongbox(数论)

题目

描述

Byteasar is a famous safe-cracker, who renounced his criminal activity and got into testing and certifying anti-burglary devices.
He has just received a new kind of strongbox for tests: a combinatorial safe.
A combinatorial safe is something different from a combination safe, even though it is opened with a rotary dial.
The dial can be set in n n different positions, numbered from 0 to n1 n − 1 .
Setting the dial in some of these positions opens the safe, while in others it does not.
And here is the combinatorial property, from which the name comes from:
if x x and y are opening positions, then so is (x+y) mod n ( x + y )   m o d   n too; note that is holds for x=y x = y as well.
Byteasar tried k k different positions of the dial: m1,m2,,mk.
The positions m1,m2,,mk1 m 1 , m 2 , ⋯ , m k − 1 did not open the safe,only the last position mk m k did.
Byteasar is already tired from checking these k k positions and has thus absolutely no intention of trying the remaining ones.
He would like to know however, based on what he already knows about the positions he tried, what is the maximum possible number of positions that open the safe.
Help him by writing an appropriate program!

输入

The first line of the standard input gives two integers n and k k , separated by a single space, 1k250 000, kn1014 k ≤ n ≤ 10 14 .
The second line holds kk different integers, also separated by single spaces, m1,m2,,mk m 1 , m 2 , ⋯ , m k , 0mi<n 0 ≤ m i < n .
You can assume that the input data correspond to a certain combinatorial safe that complies with the description above.
In tests worth approximately 70% of the points it holds that k1000 k ≤ 1000 .
In some of those tests, worth approximately 20% of the points, the following conditions hold in addition: n108 n ≤ 10 8 and k100 k ≤ 100 .

输出

Your program should print out to the first and only line of the standard output a single integer: the maximum number of the dial’s positions that can open the safe.

输入样例

42 5
28 31 10 38 24

输出样例

14

解题思路

首先说明两个结论:

结论1:如果 x x 是密码,那么gcd(x,n)也是密码。
证明:
d=gcd(x,n) d = g c d ( x , n )
由题意,因 x x 是密码,故 2x%n,3x%n,,kx%n 均是密码
又由贝祖定理推论(不定方程 ax+by=c a x + b y = c 有整数解的充要条件是 gcd(a,b)|c g c d ( a , b ) | c ), xk+nc=d x k + n c = d 一定有整数解(未知数是 k k c),那么 kZ ∃ k ∈ Z 使得 kxd(mod n) k x ≡ d ( m o d   n ) ,故 d d 也是密码

结论2:如果x,y是密码,那么 gcd(x,y) g c d ( x , y ) 也是密码。
证明:
d=gcd(x,y) d = g c d ( x , y )
由题意,因 x,y x , y 是密码,易知 (px+qy)%n ( p x + q y ) % n 也是密码( p,q0 p , q ⩾ 0
又由贝祖定理推论, xp+yq=d x p + y q = d 一定有整数解(未知数是 pq p 和 q ),那么  p,qZ ∃   p , q ∈ Z 使得 px+qyd(mod n) p x + q y ≡ d ( m o d   n ) ,故 d d 也是密码

再看看这道题,设 x,y 均为密码且 x x 是所有密码中最小的,若xy,则 gcd(x,y)<x g c d ( x , y ) < x gcd(x,y) g c d ( x , y ) 是密码(结论2),与假设矛盾,故 x|y x | y 。因此,这组密码为 x,2x,3x,,kx (kx<n) x , 2 x , 3 x , ⋯ , k x   ( k x < n ) ————①
d=gcd(mk,n) d = g c d ( m k , n ) ,由于 mk m k 是密码,根据结论1, d d 也是密码,又由①得:x|d(仍设 x x 是所有密码中最小的那个)

但是这道题给了一些限制条件:
1. 要求最多有多少密码,故我们希望x尽量的小,密码数量为 n/x n / x
2. 给出了 k1 k − 1 个不是密码的数字,故 x x 不能整除 m1,m2,,mk1

综上所述,这道题的实现方法是:在根号的时间里暴力枚举处理出 gcd(mk,n) g c d ( m k , n ) 的所有因数,去除能整除 m1,m2,,mk1 m 1 , m 2 , ⋯ , m k − 1 中任意一个的数,设剩下的数中最小的为 x x ,则答案就是 n/x


Code

(洛谷上卡时过了……应该可以优化一下算法)

#include<algorithm>
#include<cstdio>
#include<cmath>

using namespace std;

typedef long long LL;

LL n, k, a[250005], d, q[250005], mn;

LL gcd(LL a, LL b){
    if(a < b)   a ^= b, b ^= a, a ^= b;
    return b == 0 ? a : gcd(b, a % b);
}

int main(){
    scanf("%lld%lld", &n, &k);
    for(int i = 1; i <= k; i++) scanf("%lld", &a[i]);
    d = gcd(a[k], n);
    for(LL i = 1; i <= sqrt(d); i++)
        if(d % i == 0){
            q[++q[0]] = i;
            if(i * i != d)
                q[++q[0]] = d / i;
        }
    sort(q+1, q+q[0]+1);
    for(int i = 1; i < k; i++)
        for(int j = 1; j <= q[0]; j++)
            if(q[j] && a[i] % q[j] == 0)
                q[j] = 0;
    for(mn = 1; mn <= q[0]; mn++)
        if(q[mn])   break;
    printf("%lld\n", n / q[mn]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值