Spare Tire——分解质因子+容斥

题目链接:2018ACM-ICPC沈阳网络赛

题目描述:
这里写图片描述
题目思路:分解质因子+容斥
首先推出an的公式为n(n+1),由题目的思路我们是要求出1~n的数中与m互质的数的a序列的和,那可以求出an的前n项和为n(n+1)(2n+1)/6+n(n+1)/2,然后找出与小于n与m互质的数之和sum,用前一项减去后一项就是解。
所以将m分解质因数,由质因数推出小于n且与m不互质的数,如找到一个质因子k,则2*k,3*k……n/k*k都是满足条件的不互质的数,共有n/k项,因此这个因子对答案的贡献程度为(k*n)^2+k*n的前n项之和,这个值为:k*k*n*(n+1)(2*n+1)/6+k*n(n+1)
最后,因为一个数有可能会被许多质因子筛选掉,我们要枚举所有情况并用容斥的方法减去重复的值。
注意细节:在很大的数中除运算取模记得取逆元,防止爆long long 。

代码和思路均参考于大佬的博客:(https://blog.csdn.net/Lngxling/article/details/82530798)
果然蒟蒻就算是有了思路也写不好代码T T

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007
ll prime[10100];
int pl=0;
bool vis[10100];
ll n,m;

/*
    分解质因子+容斥
*/

//素数表
void getprime()
{
    for(ll i=2;i<10010;i++)
    {
        if(vis[i]==false)
        {
            prime[++pl]=i;
        }
        for(int j=1;j<=pl&&i*prime[j]<10010;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
            break;
        }
    }
}
ll num[15];
int tot;
//分解质因子
void bre(ll n)
{
    tot=0;
    for(int i=1;i<=pl&&prime[i]*prime[i]<=n;i++)
    {
        if(n%prime[i]==0)
        {
            num[tot++]=prime[i];
            while(n%prime[i]==0)
            {
                n/=prime[i];
            }
        }
        if(n==1)
        break;
    }
    if(n!=1)
    {
        num[tot++]=n;
    }
}

//取逆元,用到快速幂取模
ll inv2,inv3,inv6;
ll fpow(ll a,ll b)
{
    ll ans=1;
    ll tmp=a%mod;
    while(b)
    {
        if(b&1)
        ans=ans*tmp%mod;
        tmp=tmp*tmp%mod;
        b/=2;
    }
    return ans;
}
void solve()
{
    ll ans=0;
    //取所有组合的情况
    for(int i=0;i<(1<<tot);i++)
    {
        int cnt=0;
        ll sum=1;
        for(int j=0;j<tot;j++)
        {
            if(i&(1<<j))
            {
                cnt++;
                sum*=num[j];
            }
        }
        ll k=n/sum;
        sum%=mod;
        //计算公式,求(sum*n)^2+sum*n的前n项之和
        ll p=(1+k)*k%mod*inv2%mod*sum%mod;
        ll q=k*(k+1)%mod*(2*k+1)%mod*inv6%mod*sum%mod*sum%mod;
        //容斥
        if(cnt&1)
        {
            ans-=p;
            if(ans<0)
                ans+=mod;
            ans-=q;
            if(ans<0)
                ans+=mod;
        }
        else
        {
            ans=(ans+p);
            if(ans>mod)
                ans-=mod;
            ans+=q;
            if(ans>mod)
                ans-=mod;
        }
    }
    printf("%lld\n",ans);
}
int main()
{
    getprime();
    //记得取逆元
    inv2=fpow(2,mod-2);
    inv6=fpow(6,mod-2);
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        bre(m);
        solve();
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/Q755100802/article/details/82633760