题目描述
华华刚刚帮月月完成了作业。为了展示自己的学习水平之高超,华华还给月月出了一道类似的题:

⊕符号表示异或和,详见样例解释。
虽然月月写了个程序暴力的算出了答案,但是为了确保自己的答案没有错,希望你写个程序帮她验证一下。
输入描述:
输入一个正整数N。
输出描述:
输出答案Ans。
输入
3

输出
18

说明

示例2
输入
2005117
输出
复制
863466972
备注:

PS:第一次遇见这种题目直接傻眼了,不知道这是一个积性函数,这个概念也没听说过,然后取学习了相关知识,不仅被这个题目所折服,这个题目实在太巧妙了。

推荐罗永军老师的博客积性函数的前世今生

1.首先做这个题目肯定需要快速幂,下面给出模板

ll fastpow(ll a, ll b)
{
   
    ll ans = 1;
    while (b)
    {
   
        if (b & 1)
            ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans;
}

2.其次,我们需要了解欧拉筛与积性函数之间的关系,这个题目需要求前n项的异或和就需要用到积性函数的性质,加上欧拉筛就能很好的解决这个问题,并且时间复杂度很低,下面给出欧拉筛的代码
欧拉筛

int prime[MAXN];              //保存质数
int vis[MAXN];                //记录是否被筛
int euler_sieve(int n){
          //欧拉筛。返回质数的个数。
	int cnt = 0;              //记录质数个数
	memset(vis,0,sizeof(vis));
	memset(prime,0,sizeof(prime));
	for(int i=2;i<=n;i++){
                //检查每个数,筛去其中的合数
		if(!vis[i]) prime[cnt++]=i;    //如果没有筛过,是质数,记录。第一个质数是2
		for(int j=0; j<cnt; j++){
         //用已经得到的质数去筛后面的数
			if(i*prime[j] >n)  break;  //只筛小于等于n的数
			vis[i*prime[j]]=1;         //关键1。用x的最小质因数筛去x
			if(i%prime[j]==0)  break;  //关键2。如果不是这个数的最小质因子,结束
		}
	}
	return cnt;                        //返回小于等于n的素数的个数
}

欧拉筛求质数+最小质因子

int prime[MAXN];      //记录质数
int vis[MAXN];      //记录最小质因子
int euler_sieve(int n){
        
	int cnt=0;
	memset(vis,0,sizeof(vis));
	memset(prime,0,sizeof(prime));
	for(int i=2;i<=n;i++){
                  
		if(!vis[i]){
    vis[i]=i; prime[cnt++]=i;}    //vis[]记录x的最小质因子
		for(int j=0; j<cnt; j++){
          
			if(i*prime[j] >n)  break;       
			vis[i*prime[j]]=prime[j];              //vis[]记录x的最小质因子
			if(i%prime[j]==0)
                break;                  
		}
	}
	return cnt;
}

以上代码都是在罗永军老师博客里参考的,老师讲的十分详细并且通俗易懂,十分感谢老师,也很幸运偶然之间得到了老师博客的地址,必回常常拜读老师的大作

罗永军老师的博客地址

有了以上的基础,那么就能很好的解决这个题目,做完这个题目之后,不禁感叹,实在太巧妙了!!!

AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 13e6 + 10;
const int mod = 1e9 + 7;

ll fastpow(ll a, ll b) //快速幂
{
   
    ll ans = 1;
    while (b)
    {
   
        if (b & 1)
            ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans;
}
ll prime[maxn], fac[maxn], cnt;
bool vis[maxn];

void solve(ll n)
{
   
    fac[1] = 1; //初始化
    for (int i = 2; i <= n; ++i)
    {
   
        if (!vis[i]) //如果这个数没有被访问
        {
   
            prime[++cnt] = i;       //记录最小质数,用来筛去后面的数
            fac[i] = fastpow(i, n); //求解当前的函数值
        }
        for (int j = 1; j <= cnt && i * prime[j] <= n; ++j)
        {
   
            vis[i * prime[j]] = 1;
            fac[i * prime[j]] = fac[i] * fac[prime[j]] % mod; //公式
            if (i % prime[j] == 0)                            //如果它不是这个数的最小质因子就跳过
                break;
        }
    }
}
int main()
{
   
    ll n, ans = 0;
    scanf("%lld", &n);
    solve(n);
    for (int i = 1; i <= n; ++i)
        ans ^= fac[i];
    printf("%lld\n", ans);
}
人生总是充满了惊喜和失落 ,有恰到好处的遇见,也有撕心裂肺的怀念,但时间总是向前,没有一丝可怜,不论剧终还是待续,愿都能以梦为马,不负此生~