您的位置:首页 > 其它

JZOJ 4813 【NOIP2016提高A组五校联考2】running

2016-10-05 21:53 525 查看

running

题目大意

学校的操场可以看成一个由n个格子排成的一个环形,格子按照顺时针顺序从0到n-1标号。

小胡观察到有m个同学在跑步,最开始每个同学都在起点(即0号格子),每个同学都有个步长ai,每跑一步,每个同学都会往顺时针方向前进ai个格子。由于跑道是环形的,如果一个同学站在n-1这个格子上,如果他前进一个格子,他就会来到0。

求永远不会被跑到的格子的数目。

数据范围

对于100%的数据,1<=n<=109 ,1<=m<=50 , 1<=ai<=n

题解

首先我们先证明一个定理:第i个同学经过的格子一定是gcd(ai,n)的倍数。

这个易证:设gcd(ai,n)=c

ai*k1 mod n=x

ai * k1-k2 * n=x

ai∗k1c * c-k2∗nc* c=x

ai∗k1−k2∗ngcd(ai,n)*gcd(ai,n)=x

其中ai∗k1−k2∗ngcd(ai,n)很显然 是整数,得证!

接下来我们需要证明第i个同学一定会经过所有是gcd(ai,n)的倍数的格子。

首先,第i个同学第一次走到自己走过的格子一定是0(此结论非常显然,证明略,请读者自行思考),此时走过的长度为lcm(ai,n),每次跑步的步长是ai,所以一次循环中一共跑了lcm(ai,n)ai次,即

ai∗ngcd(ai,n)∗ai=ngcd(ai,n),

刚好不重复地把所有是gcd(ai,n)的倍数的格子都走了一遍(结合一开始证明的结论可得出)。

有了这个结论,这题就容易多了,通过这个结论,我们就可以算出一定会被经过的格子数目,如果算成∑mi=1ngcd(ai,n),那就会把某些格子算重复了。

那我们怎么办了?

用格子的位置i与n的最小公约数来分类。

枚举一个n的约数d,若存在一个i,使得gcd(ai,n)|d 说明所有gcd(i,n)=d的格子都能被到达,这样的格子个数等同于满足gcd(id,nd)=1的个数,即与nd互质的数字的个数,即ϕ(nd)。(d=gcd(i,n))

这样算的话每个合法格子既不会被算重也不会被算漏。

Code(Pascal)

var
a,g:array[0..50] of int64;
n,m,ljz,ans,o:int64;
i,l:longint;
ys:array[0..100,1..2] of int64;
function gcd(a,b:int64):int64;
var
t:int64;
begin
repeat
t:=b;
b:=a mod b;
a:=t;
until b=0;
exit(a);
end;
procedure js(p:int64);
var
i,k:longint;
begin
p:=n div p;
k:=p;
for i:=2 to trunc(sqrt(p)) do
if k mod i=0 then
begin
p:=(p div i)*(i-1);
while k mod i=0 do
k:=k div i;
end;
if k>1 then p:=(p div k)*(k-1);
ans:=ans+p;
end;
begin
readln(n,m);
for i:=1 to m do
begin
read(a[i]);
g[i]:=gcd(a[i],n);
end;
for i:=1 to trunc(sqrt(n)) do
if n mod i=0 then
begin
for l:=1 to m do
if i mod g[l]=0 then
begin
js(i);
break;
end;
if i<>sqrt(n) then
for l:=1 to m do
if n div i mod g[l]=0 then
begin
js(n div i);
break;
end;
end;
writeln(n-ans);
end.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: