碰到个积性函数好题。
华华给月月出题:https://ac.nowcoder.com/acm/problem/23047
题目描述
华华刚刚帮月月完成了作业。为了展示自己的学习水平之高超,华华还给月月出了一道类似的题:
⊕符号表示异或和,详见样例解释。
虽然月月写了个程序暴力的算出了答案,但是为了确保自己的答案没有错,希望你写个程序帮她验证一下。
输入描述:
输入一个正整数N。
输出描述:
输出答案Ans。
示例1
输入
3
输出
18
说明
N=3时,1^3=1,2^3=8,3^3=27,异或和为18。
示例2
输入
2005117
输出
863466972
备注:
1≤N≤1.3×10^7
思路
典型的空间换时间积性函数题。
已知每个合数都可用若干个质数表示,且
n m = a m ∗ b m n^m = a^m * b^m nm=am∗bm
n = a ∗ b n = a * b n=a∗b
两式成立。
所以我们只需用线性筛,求出每个质数的m次方,同时记录每个合数一个质数因子(方便后续分解合数),最后遍历即可。
PS:注意数组复用,空间超限,这里选择复用的是标记合数用的f数组。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007
#define maxn 13000000
int prime[1000007];
int f[maxn+7];
ll n;
int p[maxn+7];
ll ans=1;
ll ksm(ll a,ll b)
{
ll r=1,base=a;
while(b!=0)
{
if(b%2)
r=r*base%mod;
base=base*base%mod;
b/=2;
}
return r%mod;
}
void shai()
{
for(int i=2;i<=maxn;i++)
{
if(!f[i])
{
prime[++prime[0]]=i;
f[i]=(int)ksm((ll)i,n);
}
else
{
f[i]=0;
}
for(int j=1;j<=prime[0]&&i*prime[j]<=maxn;j++)
{
f[i*prime[j]]=1;
p[i*prime[j]]=prime[j];
if(i%prime[j]==0)
{
break;
}
}
}
}
int dfs(int x,int y)
{
return (int)((ll)f[x]*(f[y]==0?f[y]=dfs(p[y],y/p[y]):f[y])%mod);
}
int main()
{
ios::sync_with_stdio(0);
cin>>n;
shai();
for(int i=2;i<=n;i++)
{
if(f[i])
{
ans=(ans^f[i]);
}
else
ans=(ans^dfs(p[i],i/p[i]));
}
cout<<ans<<endl;
return 0;
}