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

来源:互联网 发布:花都特大网络诈骗案 编辑:程序博客网 时间:2024/05/23 19:08

题目

小胡同学是个热爱运动的好孩子。
每天晚上,小胡都会去操场上跑步,学校的操场可以看成一个由n个格子排成的一个环形,格子按照顺时针顺序从0 到n- 1 标号。
小胡观察到有m 个同学在跑步,最开始每个同学都在起点(即0 号格子),每个同学都有个步长ai,每跑一步,每个同学都会往顺时针方向前进ai 个格子。由于跑道是环形的,如果一个同学站在n-1 这个格子上,如果他前进一个格子,他就会来到0。
他们就这样在跑道上上不知疲倦地跑呀跑呀。小胡同学惊奇地发现,似乎有些格子永远不会被同学跑到,他想知道这些永远不会被任何一个同学跑到的格子的数目,你能帮帮他
吗?(我们假定所有同学都跑到过0 号格子)。

分析

首先对于一个人 i, 显然,那么它所能到达的格子一定是gcd(ai,n)的倍数。
所以我们枚举n的约数d,如果有一个i,gcd(ai,n)|d,说明所有gcd(j,n)=d 的格子都能被到达,答案加上 φ(nd) 即可。

#include <cmath>#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <queue>const int maxlongint=2147483647;const int mo=1000000007;const int N=55;using namespace std;int a[N],n,m,ans;int gcd(int x,int y){    if(y==0) return x;        if(x<y) return gcd(y,x);        else return gcd(y, x%y); }int phi(int x){    int sum=x,e=x;    for(int i=2;i<=int(sqrt(e));i++)    {        if(x%i==0)        {            sum=sum/i*(i-1);            while(x%i==0) x/=i;        }    }    if(x>1)    {        sum=sum/x*(x-1);    }    return sum;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++) scanf("%d",&a[i]);    for(int i=1;i<=int(sqrt(n));i++)    {        if(n%i==0)        {        for(int j=1;j<=m;j++)            if(i%gcd(a[j],n)==0)            {                ans+=phi(n/i);                break;            }        for(int j=1;j<=m;j++)            if((n/i)%gcd(a[j],n)==0)            {                ans+=phi(i);                break;            }        }    }    cout<<n-ans<<endl;}
1 0