hdu 5514 Frog(容斥原理)

题目链接: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 long
using 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);
    }
}



你可能感兴趣的:(数论,容斥原理)