hdu 5514 Frog(容斥原理)

来源:互联网 发布:人工智能少年普及知识 编辑:程序博客网 时间:2024/06/05 17:04

题目链接:hdu 5514

题目大意:N只青蛙,M个石头构成一个环,第i只青蛙每一次向后跳Ai个石头,即下一步的位置是(当前位置+Ai) mod m,最开始的时候所有青蛙都在位置0,每只青蛙会跳无数次,问最终所有被青蛙跳过的石头的编号之和是多少?

解题思路:i只青蛙会踩过的石头编号分别为0,gcd(Ai, m), 2 * gcd(Ai, m) ……一共有m/gcd(Ai,m)-1个,这个的和很是容易用等差数列求和计算出来……但是不同的i跳过的石头有重复! 去重的方法是容斥原理(莫比乌斯我不还不会。。),求出m的所有因数,记录每个gcd(Ai,m)的贡献,一边求和一边除去多算的贡献,用数组c[]记录多算的贡献,v[]记录贡献,详见代码。

ps.1~10^9的所有数中,约数最多的数有1344个约数,所以O(n^2)不会超时。

</pre><pre class="cpp" name="code">#include<stdio.h>#include<algorithm>#include<vector>#include<math.h>#include<string.h>#define ll long longusing namespace std;vector<int>ft;int v[2005],c[2005],ca=0, a;void qft(int m){    ft.clear();    int n=(int)sqrt(m)+1;    for(int i=1; i<n; i++)    {        if(!(m%i))        {            ft.push_back(i);            if(m/i!=i)ft.push_back(m/i);        }    }}int gcd(int a,int b){    while(a&&b)    {        if(a>b)a=a%b;        else b=b%a;    }    return a+b;}int find(int m){    return lower_bound(ft.begin(),ft.end(),m)-ft.begin();}int main(){    int t,n,m;    scanf("%d",&t);    while(t--)    {        memset(v,0,sizeof(v));        memset(c,0,sizeof(c));        scanf("%d%d",&n,&m);        qft(m);        sort(ft.begin(),ft.end());        for(int i=0; i<n; i++)        {            scanf("%d",&a);            int d=gcd(a,m);            v[find(d)]=1;//c语言函数不能嵌套!否则会tle        }        int len=ft.size();        for(int i=0; i<len; i++)        {            if(v[i])                for(int j=i+1; j<len; j++)                    if(!v[j]&&ft[j]%ft[i]==0)v[j]=1;        }        ll ans=0;        for(int i=0; i<len; i++)        {            ll x=v[i]-c[i];            if(x==0)continue;            ans+=(ll)(m/ft[i]-1)*(m/ft[i])/2*x*ft[i];//小心越界啊大兄弟            for(int j=i; j<len; j++)                if(ft[j]%ft[i]==0)c[j]+=x;        }        printf("Case #%d: %I64d\n",++ca,ans);    }}


1 0
原创粉丝点击