HDU 4059 The Boss on Mars(质数分解+容斥)

题目自行搜索

题意:给你一个数n,求小于n并且与n互质的所有数的4次方之和。

分析:

n的数据量达到1e8,O(n)的复杂度都悬,由此想到因数分解O(sqrt(n))。

那么把一个数因数分解之后,是一个 若干质数的若干次幂相乘的形式,

那么对这个分解式的每一个质数来说,如果某个小于n的数是某个质因数的倍数的话,那么这个小于n的数就不是答案里出现的数,那么对于分解出的每个质因数都要遍历,只出现1个的,出现2个的,一直到出现所有质因数的所有排列情况,然后出现奇数次的加,出现偶数次的减,就可以保证不重不漏。因为质数分解后不同的质因子个数最多是9个,所有排列情况是2^9种,dfs打出所有情况进行操作。(dfs队友写的,本弱不会dfs)

其中有一点自以为比较巧妙的是:假如分解的质因数里有2,那么是要去掉所有2的倍数,在答案中出现的形式是

2^4+4^4+6^4+8^4……转换一下就是2^4(1^4+2^4+3^4+4^4+……)括号里的数的数量就是k/2,那么就可以用一个函数来算前n项的4次方累加和。

#include <bits/stdc++.h>

using namespace std;
const int MOD=1e9+7;
typedef long long LL;
LL Stack[1000],top,Cnt[1000],tmp[1000];
LL ans,kk;
void fenjie(LL k)///素数分解
{
    top=1;
    memset(Cnt,0,sizeof(Cnt));
    for(LL i=2;i*i<=k;i++)if(k%i==0){
        while(k%i==0)Cnt[top]++,k/=i;
        Stack[top++]=i;
    }
    if(k>1){
        Cnt[top]++;
        Stack[top++]=k;
    }
}
LL ff(LL a)///求a的四次方取模
{
    LL ans=(((a*a)%MOD*a)%MOD*a)%MOD;
    return ans;
}
LL quickpow(LL m,LL n)///求逆元的快速幂
{
    LL b=1;
    while(n>0)
    {
        if(n&1)b=(b*m)%MOD;
        n=n>>1;
        m=(m*m)%MOD;
    }
    return b;
}
LL f(LL a)///求x的四次方的前n项和,一个公式而已
{
    LL ans=((((((6*quickpow(a,5))%MOD+(15*quickpow(a,4))%MOD)+(10*quickpow(a,3)))%MOD-a+MOD)%MOD)*quickpow(30,MOD-2))%MOD;
    ans=(ans+MOD)%MOD;
    return ans;
}
LL q[11];
void dfs(LL Stack[],int top,int s,int e,int k)///dfs遍历质因数的所有情况,进行容斥
{
    q[k]=Stack[s];
    if(k==e)
    {
        //sum++;
        if(e%2==1)
        {
            LL te=1;
            for(LL i=1;i<=e;i++)
            te*=q[i];//cout<<q[i]<<" ";}cout<<endl;
            //cout<<endl;
            LL temp=kk/te;//cout<<"temp="<<temp<<" ";cout<<f(temp)*ff(te)<<" ";
                ans=(ans+(f(temp)*ff(te))%MOD+MOD)%MOD;///奇数次加
                //cout<<"ans="<<ans<<endl;
        }
        else
        {
            LL te=1;
            for(LL i=1;i<=e;i++)
            te*=q[i];//cout<<q[i]<<" ";}cout<<endl;
            LL temp=kk/te;//cout<<"temp="<<temp<<" ";cout<<f(temp)*ff(te)<<" ";
                ans=(ans-(f(temp)*ff(te))%MOD+MOD)%MOD;///偶数次减
                //cout<<"ans="<<ans<<endl;
            //cout<<endl;
        }
    }
    else
    {
        for(int i=1;i+s<top;i++)
        {
            k++;
            dfs(Stack,top,s+i,e,k);
            k--;
        }
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%lld",&kk);
        fenjie(kk);
        LL sum=f(kk);
         ans=0;
         for(int e=1;e<top;e++)
    {
        for(int i=1;i<top;i++)
        {
                dfs(Stack,top,i,e,1);
                //cout<<ans<<endl;
                //system("pause");
        }
    }
        sum=((sum-ans)+MOD)%MOD;
        printf("%lld\n",sum);
    }
    return 0;
}




二进制枚举的方法:

#include <bits/stdc++.h>

using namespace std;
const int MOD=1e9+7;
typedef long long LL;
LL Stack[1000],top,Cnt[1000],tmp[1000];
LL ans,kk;
void fenjie(LL k)///素数分解
{
    top=0;
    memset(Cnt,0,sizeof(Cnt));
    for(LL i=2;i*i<=k;i++)if(k%i==0){
        while(k%i==0)Cnt[top]++,k/=i;
        Stack[top++]=i;
    }
    if(k>1){
        Cnt[top]++;
        Stack[top++]=k;
    }
}
LL ff(LL a)///求a的四次方取模
{
    LL ans=(((a*a)%MOD*a)%MOD*a)%MOD;
    return ans;
}
LL quickpow(LL m,LL n)///求逆元的快速幂
{
    LL b=1;
    while(n>0)
    {
        if(n&1)b=(b*m)%MOD;
        n=n>>1;
        m=(m*m)%MOD;
    }
    return b;
}
LL f(LL a)///求x的四次方的前n项和,一个公式而已
{
    LL ans=((((((6*quickpow(a,5))%MOD+(15*quickpow(a,4))%MOD)+(10*quickpow(a,3)))%MOD-a+MOD)%MOD)*quickpow(30,MOD-2))%MOD;
    ans=(ans+MOD)%MOD;
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%lld",&kk);
        fenjie(kk);
        LL sum=f(kk);
         ans=0;
        for(LL i=1;i<(1<<top);i++)///二进制枚举,2^top的二进制0,1对应j下标的因子取还是不取
        {
            LL num=0;
            LL te=1;
            for(LL j=0;j<top;j++)
            {
                if(i&(1<<j))
                {
                    num++;///记录二进制中1的个数
                    te=(Stack[j]*te)%MOD;
                }
            }
            LL temp=kk/te;
            if(num&1)
            ans=(ans+(f(temp)*ff(te))%MOD+MOD)%MOD;///奇数次加
            else
             ans=(ans-(f(temp)*ff(te))%MOD+MOD)%MOD;///偶数次减
        }
        //cout<<ans<<endl;
        sum=((sum-ans)+MOD)%MOD;
        printf("%lld\n",sum);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值