【JZOJ 4813】running

Description

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

Solution

有等式: xaiyngcd(ai,n)( modn) (不要问我为什么)
所以,很显然,i点可以走到的所有点一定是它与n的gcd,
我们统计有多少个点可以走到,
枚举每个n个因数d,看看有没有 ai 符合条件,如有符合的就说明有 φ(d) 个点可以走到(是phi的原因是防止统计重复)

Code

#include<cstdio>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=60;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int a[N],b[N][2],b0;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
bool OK(int q)
{
    fo(i,1,m)if(0==q%a[i])return 1;
    return 0;
}
int phi(int q)
{
    int w=q,ans=q;
    for(int i=2;i*i<=w;i++)if(w%i==0)
    {
        while(w%i==0)w/=i;
        ans=ans/i*(i-1);
    }
    if(w>1)ans=ans/w*(w-1);
    return ans;
}
void ss(int q,int e)
{
    if(q>b0)
    {
        if(OK(e))ans+=phi(n/e);
        return;
    }
    ss(q+1,e);
    fo(i,1,b[q][0])ss(q+1,e*=b[q][1]);
}
int main()
{
    freopen("running.in","r",stdin);
    freopen("running.out","w",stdout);
    int q,w;
    read(n),read(m);
    fo(i,1,m)a[i]=gcd(read(q),n);
    for(w=2,q=n;w*w<=q;w++)if(q%w==0)
    {
        b[++b0][1]=w;
        while(q%w==0)b[b0][0]++,q/=w;
    }
    if(q>1)b[++b0][1]=q,b[b0][0]=1;
    ss(1,1);
    printf("%d\n",n-ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值