• 7.12 NOI模拟赛 积性函数求和 数论基础变换 莫比乌斯反演


    avatar
    avatar

    神题!

    一眼powerful number 复习了一下+推半天。

    可以发现G函数只能为(sum_{d}[d|x]d)

    不断的推 可以发现最后需要求很多块G函数的前缀和 发现只有(sqrt(n))的复杂度。

    于是自闭了。不过这个做法可以跑过(1e9) 第二个subtask没有那么严格所以可以跑过 不过我CE了2333...

    也没考虑(min_25)筛 可能学的不太精通。。

    正解是发现 (f(n)=(p^{k1}+1)(p^{k2}+1)...)

    然后 将其展开 求每个数字对n的贡献 即枚举上述状态的每一项.

    那么在求前缀和中i可以被统计到答案中的标志为存在令一个数字j 满足(icdot jleq n)((i,j)=1)

    那么其实答案为 (sum_{i=1}^nsum_{j=1}^{frac{n}{i}}[(i,j)=1]i)

    莫比乌斯反演一下可以得到.

    (sum_{x=1}^nmu(x)sum_{i=1}^{frac{n}{x}}ifrac{n}{icdot x^2})

    这其实就可以做了。

    考虑到(frac{n}{icdot x^2})有值 那么显然 (xleq sqrt n)

    (ileq frac{n}{x^2})

    那么上述式子枚举范围可以变一下.(sum_{x=1}^{sqrt n}mu(x)sum_{i=1}^{frac{n}{x^2}}ifrac{n}{icdot x^2})

    左边暴力枚举 右边整除分块。可以证明这样复杂度为(sqrt ncdot logn)

    可能有点卡常 考虑线性筛出 (d_n)表示(sum_{i=1}^{n}ifrac{n}{i})

    考虑右边的实际意义 约数和的前缀和 那么只需要筛出每个数字约数和 再前缀和一下即可。

    code
    //#include<bitsstdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cmath>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<vector>
    #include<algorithm>
    #include<utility>
    #include<bitset>
    #include<set>
    #include<map>
    #define ll long long
    #define db double
    #define INF 1000000000000000ll
    #define ldb long double
    #define pb push_back
    #define put_(x) printf("%d ",x);
    #define get(x) x=read()
    #define gt(x) scanf("%d",&x)
    #define gi(x) scanf("%lf",&x)
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define gc(a) scanf("%s",a+1)
    #define rep(p,n,i) for(RE int i=p;i<=n;++i)
    #define go(x) for(ll i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
    #define fep(n,p,i) for(RE ll i=n;i>=p;--i)
    #define vep(p,n,i) for(RE ll i=p;i<n;++i)
    #define pii pair<ll,ll>
    #define mk make_pair
    #define RE register
    #define P 1000000007
    #define gf(x) scanf("%lf",&x)
    #define pf(x) ((x)*(x))
    #define uint unsigned long long
    #define ui unsigned
    #define EPS 1e-4
    #define sq sqrt
    #define S second
    #define F first
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline ll read()
    {
        RE ll x=0,f=1;RE char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    const ll MAXN=4000010;
    ll n;
    int m,mod,top,INV2;
    int p[MAXN],mu[MAXN],v[MAXN],g[MAXN],w[MAXN];
    inline void prepare()
    {
    	mu[1]=g[1]=1;
    	rep(2,m,i)
    	{
    		if(!v[i])v[i]=p[++top]=i,w[i]=i,mu[i]=-1;
    		if(w[i]==i)g[i]=(g[i/v[i]]+i)%mod;
    		else g[i]=(ll)g[i/w[i]]*g[w[i]]%mod;
    		rep(1,top,j)
    		{
    			if(p[j]>m/i)break;
    			v[p[j]*i]=p[j];
    			if(v[i]==p[j])
    			{
    				w[p[j]*i]=w[i]*p[j];
    				break;
    			}
    			w[p[j]*i]=p[j];mu[p[j]*i]=-mu[i];
    		}
    	}
    	rep(1,m,i)g[i]=(g[i-1]+g[i])%mod;
    }
    inline int solve(ll n)
    {
    	if(n<=m)return g[n]*2%mod;
    	if(n>=mod)
    	{
    		ll ww,w1;
    		int ans=0;
    		for(ll i=1;i<=n;i=ww+1)
    		{
    			w1=n/i;ww=n/w1;
    			ans=(ans+(i+ww)%mod*((ww-i+1)%mod)%mod*(w1%mod))%mod;
    		}
    		return ans;
    	}
    	else
    	{
    		ll ww,w1;
    		int ans=0;
    		for(ll i=1;i<=n;i=ww+1)
    		{
    			w1=n/i;ww=n/w1;
    			ans=(ans+(i+ww)*(ww-i+1)%mod*w1)%mod;
    		}
    		return ans;
    	}
    }
    signed main()
    {
    	freopen("1.in","r",stdin);
    	get(n);get(mod);m=(int)sqrt(n*1.0);INV2=(1+mod)>>1;
    	prepare();int ans=0;
    	rep(1,m,i)if(mu[i])ans=(ans+mu[i]*(ll)i*solve(n/i/i))%mod;
    	ans=(ans+mod)%mod;put((ll)ans*INV2%mod);return 0;
    }
    
  • 相关阅读:
    暴躁游戏

    时间记录表格
    好好生活
    JAVA环境的配置
    Java简介
    markdown学习

    Arduino
    Arduino
  • 原文地址:https://www.cnblogs.com/chdy/p/13290420.html
Copyright © 2020-2023  润新知