题目自行搜索
题意:给你一个数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;
}