CCPC-Wannafly Winter Camp Day3 (Div2, onsite) F 小清新数论 欧拉函数的利用 莫比乌斯反演 杜教筛

F - 小清新数论

做法一:欧拉函数

在这里插入图片描述

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int  maxn = 1e7+9;
const LL mod = 998244353;
LL phi[maxn],miu[maxn],fac[maxn];//phi--欧拉函数表  miu--莫比乌斯函数表  fac--i最大的素因子辅助打phi表
void init()
{
	for (int i = 1; i < maxn; ++i) fac[i] = i;
	phi[1] = miu[1] = 1;
	for (int i = 2; i < maxn; ++i)
	{
		if (fac[i] == i)
			for (int j = i << 1; j < maxn; j += i)
				fac[j] = i;
		if (i / fac[i] % fac[i]) phi[i] = (fac[i] - 1)*phi[i / fac[i]], miu[i] = -miu[i / fac[i]]; //如果b质数  a%b!=0  phi(a*b) = phi(a)*b - phi(a)
		else phi[i] = fac[i] * phi[i / fac[i]], miu[i] = 0;										//当b是质数,a%b==0,phi(a*b)=phi(a)*b
	}
	for(int i=1;i<maxn;i++)phi[i]=phi[i]+phi[i-1];
}
int main(){
    init();
    LL n;
    cin>>n;
    LL NN=n;
    LL ans=0;
    for(LL i=1;i<=NN;i++){
        LL res=(phi[NN/i]*(LL)2-1)%mod;
        ans=(ans+miu[i]*res%mod)%mod;
        while(ans<0)ans+=mod;
    }
    printf("%lld\n",ans);
}

做法二:莫比乌斯反演

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=1e7+1;
ll phi[maxn],miu[maxn],vis[maxn];
void init(){
    for(int i=1;i<maxn;++i)vis[i]=i;
    phi[1]=miu[1]=1;
    for(int i=2;i<maxn;i++){
        if(vis[i]==i){
            for(int j=i<<1;j<maxn;j+=i)vis[j]=i;
        }
        if(i/vis[i]%vis[i])miu[i]= -miu[i/vis[i]];
        else miu[i]=0;
    }

    for(int i=1;i<maxn;i++)miu[i]=miu[i]+miu[i-1];
}
ll solve(int n,int m){
    ll ans=0;
    int N=min(n,m),r;
    for(int l =1;l<=N;l=r+1){
        r=min(n/(n/l),m/(m/l)); //取分块小的数
        ll res=(miu[r]-miu[l-1]+mod)%mod*(n/l)%mod*(n/l)%mod;   //miu[r]-miu[l-1]表示l~r区间miu和,
        ans=(ans+res+mod)%mod;
    }
    return ans;
}
int main(){
    init();
    int n,r;
    scanf("%d",&n);
    ll ans=0,res;
    for(int l=1;l<=n;l=r+1){
        r=n/(n/l);
        res=(miu[r]-miu[l-1]+mod)%mod*solve(n/l,n/l)%mod;
        ans=(ans+res+mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

做法三:杜教筛能过div1,跑了1423ms,对做法一中欧拉函数前n项和,欧拉函数前n项和进行杜教筛,然后套一个分块求解

#include<stdio.h>
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define INV2 499122177
using namespace std;
typedef long long ll;
const int N=1e7+20;
const int mod=998244353;
bool vis[N];
int mu[N],sum1[N];
long long phi[N],sum2[N];
int cnt,prim[N];
int e,e1;
tr1::unordered_map<long long,long long>w,w1;  //哈希 w用来求phi前缀和  w1用来求miu前缀和
void get(int maxn)
{
    phi[1]=mu[1]=1;
    for(int i=2;i<=maxn;i++)
    {
        if(!vis[i])
        {
            prim[++cnt]=i;
            mu[i]=-1;phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&prim[j]*i<=maxn;j++)
        {
            vis[i*prim[j]]=1;
            if(i%prim[j]==0)
            {
                phi[i*prim[j]]=phi[i]*prim[j];
                break;
            }
            else mu[i*prim[j]]=-mu[i],phi[i*prim[j]]=phi[i]*(prim[j]-1);
        }
    }
    for(int i=1;i<=maxn;i++)sum1[i]=sum1[i-1]+mu[i],sum2[i]=(sum2[i-1]+phi[i])%mod;   //打一个maxn的phi前缀和表 和miu前缀和表
}
int djsmu(long long x)	//	求miu前缀和
{
    if(x<=10000000)return sum1[x];
    if(w[x])return w[x];
    int ans=1;
    for(long long l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(r-l+1ll)*djsmu(x/l);
    }
    return w[x]=ans;
}
long long djsphi(long long x)	//求phi 前缀和
{
    if(x<=10000000)return sum2[x];
    if(w1[x])return w1[x];
    long long ans=x%mod*(x+1)%mod*INV2%mod;
    for(long long l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ans=(ans-(r-l+1)%mod*djsphi(x/l)+mod)%mod;
    }
    while(ans<0)ans+=mod;
    return w1[x]=ans%mod;
}
int main(){
    get(10000000);
    ll n,r;
    scanf("%lld",&n);
    ll ans=0,res;
    for(ll l=1;l<=n;l=r+1){
        r=n/(n/l);
        res=(ll)(djsmu(r)-djsmu(l-1)+mod)%mod*((djsphi(n/l)%mod*(ll)2%mod-1+mod)%mod)%mod;
        ans=(ans+res+mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值