HDU 5514 Frogs (容斥原理+因子分解)

题目链接

题意:有n只青蛙,m个石头(围成圆圈)。第i只青蛙每次只能条ai个石头,问最后所有青蛙跳过的石头的下标总和是多少?

题解:暴力肯定会超时,首先分解出m的因子,自己本身不用分,因为石头编号是0到m-1,第i只青蛙只能走到gcd(ai, m)的位置,我们就可以把m的因子提取出来,然后对青蛙能走到的因子位置打标记。两个数组,第一个数组a代表是否能走到该因子,第二个数组b表示是否已经加过了,加过了几次,遍历到那个因子的时候要加上(a[i]-b[i])倍的数,这个数很可能是负数。至于公式利用等差数列前n项和可以得出。

注意这道题是因子分解而不是素因子分解,因为对于12来说2和4是不一样的。

注意这道题不是对数进行操作而是对因子进行操作。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
const int maxn=1e5+5;
ll cnt=0,prime[maxn];
void solve(ll m)
{
    cnt=0;//注意初始化
    prime[cnt++]=1;
    for(int i=2; i<sqrt(m); i++)
    {
        if(m%i==0)
        {
            prime[cnt++]=i;
            prime[cnt++]=m/i;
        }
    }
    ll tmp=sqrt(m);
    if(tmp*tmp==m)
        prime[cnt++]=tmp;
    sort(prime,prime+cnt);
}
int main()
{
    int t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        ll n,m,data,tmp;
        scanf("%lld%lld",&n,&m);
        memset(prime,0,sizeof(prime));
        solve(m);
        ll a[maxn],b[maxn];
        memset(a,0,sizeof(a));//标记
        memset(b,0,sizeof(b));//算过了几次
        for(int i=0; i<n; i++)
        {
            scanf("%lld",&data);
            tmp=gcd(data,m);
            for(int j=0; j<cnt; j++)
            {
                if(prime[j]%tmp==0)
                    a[j]=1;
            }
        }
        ll ans=0;
        for(int i=0; i<cnt; i++)
        {
            ll num=a[i]-b[i];
            if(num!=0)
            {
                ll tmp=(m-1)/prime[i];
                ans=ans+tmp*(tmp+1)/2*prime[i]*num;
                for(int j=i; j<cnt; j++)
                {
                    if(prime[j]%prime[i]==0)
                        b[j]=b[j]+num; //注意是b表示数目
                }
            }
        }
        printf("Case #%d: %lld
",cas++,ans);
    }
    return 0;
}

 更新版:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
const int maxn=1e5+5;
ll prime[maxn],a[maxn];
int cnt=0;
void solve(ll m)
{
    memset(a,0,sizeof(a));
    memset(prime,0,sizeof(prime));
    cnt=0;
    prime[cnt++]=1;
    for(int i=2;i<sqrt(m);i++)
    {
        if(m%i==0)
        {
            prime[cnt++]=i;
            prime[cnt++]=m/i;
        }
    }
    if(ll(sqrt(m))*ll(sqrt(m))==m) prime[cnt++]=ll(sqrt(m));
    sort(prime,prime+cnt);
}
int main()
{
    int t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        ll n,m,data;
        scanf("%lld%lld",&n,&m);
        solve(m);
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&data);
            data=gcd(data,m);
            for(int j=0;j<cnt;j++)
            if(prime[j]%data==0) a[j]=1;
        }
        ll ans=0;
        for(int i=0;i<cnt;i++)
        {
            ll num=a[i];
            if(num!=0)
            {
                ll tmp=(m-1)/prime[i];
                ans+=tmp*(tmp+1)/2*prime[i]*num;
                for(int j=i+1;j<cnt;j++)
                if(prime[j]%prime[i]==0) a[j]-=num;
            }
        }
        printf("Case #%d: %lld
",cas++,ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Ritchie/p/5708729.html