1 条题解

  • 0
    @ 2025-8-24 22:24:22

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Refined_heart
    再见。

    搬运于2025-08-24 22:24:22,当前版本为作者最后更新于2020-09-08 18:41:31,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    官方题解

    update on 2020/9/9 更新了复杂度证明。

    我太菜了……题解改了好多次,麻烦管理了qwq

    对于百分之十的数据:随便过。

    下面推式子:

    i=1nj=1ngcd(i,j)i+j\sum_{i=1}^n\sum_{j=1}^n\gcd(i,j)^{i+j} $$=\sum_{d=1}^n\sum_{i=1}^n\sum_{j=1}^nd^{i+j}[\gcd(i,j)=d] $$$$=\sum_{d=1}^n\sum_{i=1}^\frac{n}{d}\sum_{j=1}^\frac{n}{d}d^{d(i+j)}[\gcd(i,j)=1] $$$$=\sum_{d=1}^n\sum_{i=1}^\frac{n}{d}\sum_{j=1}^\frac{n}{d}d^{d(i+j)}\sum_{k|\gcd(i,j)}\mu(k) $$$$=\sum_{d=1}^n\sum_{k=1}^\frac{n}{d}\mu(k)\sum_{i=1}^\frac{n}{kd}\sum_{j=1}^\frac{n}{kd}d^{kd(i+j)} $$

    T=kd:T=kd:

    $$=\sum_{T=1}^n\sum_{k|T}\mu(k)\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T}[(\frac{T}{k})^T]^{i+j} $$

    现在的问题在于$\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T}[(\frac{T}{k})^T]^{i+j}.$

    • 线性递推

    以下是@SOSCHINA大佬的思路:

    g(n)=i=1nj=1nks.g(n)=\sum_{i=1}^n\sum_{j=1}^n k^s.

    枚举s=i+j.s=i+j.

    则有:

    $$g(n)=\sum_{s=2}^{n+1}(s-1)k^s+\sum_{s=n+2}^{2n}(2n-s+1)k^s $$$$g(n+1)=\sum_{s=2}^{n+1}(s-1)k^s+\sum_{s=n+2}^{2n+2}(sn+3-s)k^s $$$$g(n+1)-g(n)=\sum_{s=n+2}^{2n}2k^s+2k^{2n+1}+k^{2n+2} $$

    第三行就是两行相减。

    对第一行的解释:[2,n+1][2,n+1]这里的数,每个数作为i+ji+j都出现了x1x-1次。因为ii可以取遍[1,x1].[1,x-1].后面的那一些,[n+2,2n][n+2,2n]会发现ii最大只能到n,n,不能再取遍x1x-1个值了。此时能取到的应该是2ns+12n-s+1种。

    对于g(n+1):g(n+1):这里是把第一个式子的最后一个值移动到了后面那个式子,方便做差。

    这时我们可以在小模数的情况下做到O(nmodO(n*mod)的预处理。

    • 化简形式

    x=(Tk)T.x=(\frac{T}{k})^T.

    则原式为$\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T} x^{i+j}.$

    像不像一个多项式。

    它就等于(x+x2+...xnT)2.(x+x^2+...x^\frac{n}{T})^2.

    于是我们可以等比数列求和解出。

    剩下的,可以做到O(nlognlogmod)O(n\log n\log mod)处理出整个式子。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1500001;
    int mod,TT;
    bitset<MAXN+1>vis;
    int p[MAXN+1],mu[MAXN+1],T[MAXN+1],cnt,n,Ans;
    inline int Mod(long long x){
    	if(x<0)return x+mod;
        if(x>=mod)return x%mod;
        return x;
    }
    inline int add(int x,int y) {return Mod(1ll*x+1ll*y+1ll*mod);}
    inline int mul(int x,int y) {return Mod(1ll*x*y);}
    inline int qpow(int a,int b) {
    	if(!b)return 1;
    	if(a<=1||b==1)return a;
    	a %= mod; 
    	int res=1;
    	while(b) {
    		if(b&1)res=mul(res,a);
    		a=mul(a,a);
    		b>>=1;
    	}
    	return res;
    }
    inline int calc(int x,int y){
    	if(y==1)return x;
    	if(x==1)return y;
    	int ans=x;
    	int inv=qpow((1-x+mod)%mod,mod-2);
    	int fm=(1-qpow(x,y)+mod)%mod;
    	ans=mul(ans,mul(fm,inv));
    	return ans;
    }
    inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);} 
    int main() {
    	scanf("%d",&TT);
    	mu[1]=1;
    	int N=MAXN;
    	for(register int i=2; i<=N; ++i) {
    		if(!vis[i])p[++cnt]=i,mu[i]=-1;
    		for(register int j=1; j<=cnt&&i*p[j]<=N; ++j) {
    			vis[i*p[j]]=1;
    			if(i%p[j]==0)break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	while(TT--) {
    		scanf("%d%d",&n,&mod);
    		N=n;Ans=0;
    		for(register int i=1; i<=N; ++i) {
    			for(register int j=i,k,x; j<=N; j+=i) {
    				k=i;if(!mu[k])continue;
    				x=qpow(j/k,j);
    				T[j]=add(T[j],mul(mu[k],Calc(x,n/j)));
    			}
    		}
    		for(register int i=1; i<=n; ++i)Ans=add(Ans,T[i]),T[i]=0;
    		printf("%d\n",Ans);
    	}
    
    	return 0;
    }
    

    由于这里是51055*10^5的数据,所以略微卡常,但笔者通过非常不精湛的卡常技术跑到了3s3s以内,所以这里的时间限制我开了3.2s3.2s.

    对等比数列进行精细处理,可以做到O(nlog2n)O(n\log^2n)的复杂度。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int MAXN=1500000;
    int mod,TT;
    bitset<MAXN<<1>vis;
    int p[MAXN<<1],mu[MAXN<<1],T[MAXN<<1],cnt,n,Ans;
    inline int Mod(long long x){
        if(x>=mod)return x%mod;
        return x;
    }
    inline int add(int x,int y) {
    	return Mod(x+y+mod);
    }
    inline int mul(int x,int y) {
    	return Mod(1ll*x*y);
    }
    inline int qpow(int a,int b) {
    	if(!b)return 1;
    	if(a<=1||b==1)return a;
    	a %= mod; 
    	int res=1;
    	while(b) {
    		if(b&1)res=mul(res,a);
    		a=mul(a,a);
    		b>>=1;
    	}
    	return res;
    }
    inline int calc(int x,int y){
    	if(y==1)return x;
    		int res=calc(x,y/2);
    	res=add(res,mul(res,qpow(x,y/2)));
    	if(y&1)res=add(res,mul(x,qpow(x,y-1)));
    	return res;
    }
    inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);} 
    signed main() {
    	scanf("%lld",&TT);
    	mu[1]=1;
    	int N=MAXN;
    	for(int i=2; i<=N; ++i) {
    		if(!vis[i])p[++cnt]=i,mu[i]=-1;
    		for(int j=1; j<=cnt&&i*p[j]<=N; ++j) {
    			vis[i*p[j]]=1;
    			if(i%p[j]==0)break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	while(TT--) {
    		scanf("%lld%lld",&n,&mod);
    		N=n;
    		Ans=0;
    		for(int i=1; i<=N; ++i) {
    			for(int j=i; j<=N; j+=i) {
    				int k=i;
    				int x=qpow(j/k,j);
    				if(!mu[k])continue;
    				T[j]=add(T[j],mul(mu[k],Calc(x,n/j)));
    			}
    		}
    		for(int i=1; i<=n; ++i)Ans=add(Ans,T[i]),T[i]=0;
    		cout<<Ans<<endl;
    	}
    
    	return 0;
    }
    

    由于常数等原因,这分代码可以拿到5050分的好成绩。但我们可以通过另一种做法将常数/复杂度降低。

    另一种做法

    观察:

    $$\sum_{d=1}^n\sum_{k=1}^\frac{n}{d}\mu(k)\sum_{i=1}^\frac{n}{kd}\sum_{j=1}^\frac{n}{kd}d^{kd(i+j)} $$$$=\sum_{d=1}^n \sum_{k=1}^\frac{n}{d} \mu(k)(d^{kd}+d^{2kd}+...+d^{\frac{n}{kd}*kd=n})^2. $$

    这里同样观察式子发现可以直接算。前一部分是O(nlnn)O(n\ln n)nn倍调和级数的复杂度,后面带上一个O(logn)O(\log n)精细处理的等比数列求求和复杂度。

    (代码中的优化即使不加也是可以过的)

    #define __AVX__ 1
    #define __AVX2__ 1
    #define __SSE__ 1
    #define __SSE2__ 1
    #define __SSE2_MATH__ 1
    #define __SSE3__ 1
    #define __SSE4_1__ 1
    #define __SSE4_2__ 1
    #define __SSE_MATH__ 1
    #define __SSSE3__ 1
    #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
    #include <immintrin.h>
    #include <emmintrin.h>
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #include <bitset>
    using namespace std;
    const int MAXN=1.5e6+10;
    int mod,T;
    bitset<MAXN+1>vis;
    int p[MAXN+1],cnt,mu[MAXN+1],N;
    inline int Mod(long long a, int pp){
        return a>=pp ? a%pp : a>=0 ? a : a+pp;
    }
    inline int add(int x,int y){return Mod( (1ll+x+y+mod-1ll),mod);}
    inline int mul(int x,int y){return Mod(1ll*x*y,mod);}
    void pretreatment(){
    	mu[1]=1;
    	for(int i=2;i<=MAXN;++i){
    		if(!vis[i])p[++cnt]=i,mu[i]=-1;
    		for(int j=1;j<=cnt&&i*p[j]<=MAXN;++j){
    			vis[i*p[j]]=1;
    			if(Mod(i,p[j])==0)break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    }
    inline int qpow(int a,int b){
    	if(!b)return 1;
    	if(a<=1||b==1)return a;
    	int res=1;
    	while(b){
    		if(b&1)res=mul(res,a);
    		a=mul(a,a);b>>=1;
    	}
    	return res;
    }
    inline int calc(int x,int y){
    	if(y==1)return x;
    	int res=calc(x,y>>1);
    	res=add(res,mul(res,qpow(x,y>>1)));
    	if(y&1)res=add(res,mul(x,qpow(x,y-1)));
    	return res;
    }
    
    inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);} 
    int ssolve(int n,int d){
    	int res=0;
    	for(register int l=1;l<=n;++l){
    		if(!mu[l])continue;
    		res=add(res,mul(mu[l],Calc(qpow(d,l),n/l)));
    	}
    	return res;
    }
    int solve(int n){
    	int ans=0;
    	for(register int l=1;l<=n;l++){
    		ans=add(ans,ssolve(n/l,qpow(l,l)));
    	}
    	return ans;
    }
    signed main(){
    	scanf("%lld",&T);
    	pretreatment();
    	for(;T;T--){
    		scanf("%lld%lld",&N,&mod);
    		printf("%lld\n",solve(N));
    	}
    	return 0;
    }
    

    可以用整除分块减少循环中乘法的使用,对代码速度可能有一定的提升。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1.5e6+10;
    int mod,T;
    bitset<MAXN+1>vis;
    int p[MAXN+1],cnt,mu[MAXN+1],N;
    inline int Mod(long long a, int pp){return a>=pp ? a%pp : a>=0 ? a : a+pp;}
    inline int add(int x,int y){return Mod( (1ll+x+y+mod-1ll),mod);}
    inline int mul(int x,int y){return Mod(1ll*x*y,mod);}
    inline int qpow(int a,int b){
    	if(!b)return 1;
    	if(a<=1||b==1)return a;
    	a=Mod(a,mod);
    	int res=1;
    	while(b){
    		if(b&1)res=mul(res,a);
    		a=mul(a,a);b>>=1;
    	}
    	return res;
    }
    inline int calc(int x,int y){
    	if(y==1)return x;
    	int res=calc(x,y>>1);
    	res=add(res,mul(res,qpow(x,y>>1)));
    	if(y&1)res=add(res,mul(x,qpow(x,y-1)));
    	return res;
    }
    inline int Calc(int x,int y){int ans=calc(x,y);return mul(ans,ans);} 
    int ssolve(int n,int d){
    	int res=0;
    	for(register int l=1,r;l<=n;l=r+1){
    		r=(n/(n/l));
    		int D=n/l;
    		for(int i=l;i<=r;++i){
    			if(!mu[i])continue;
    			res=add(res,mul(mu[i],Calc(qpow(d,i),D)));
    		}
    	}
    	return res;
    }
    int solve(int n){
    	int ans=0;
    	for(register int l=1,r;l<=n;l=r+1){
    		r=(n/(n/l));
    		int D=n/l;
    		for(int i=l;i<=r;++i)ans=add(ans,ssolve(D,qpow(i,i)));
    	}
    	return ans;
    }
    int main(){
    	scanf("%d",&T);
    	mu[1]=1;
    	for(register int i=2;i<=MAXN;++i){
    		if(!vis[i])p[++cnt]=i,mu[i]=-1;
    		for(register int j=1;j<=cnt&&i*p[j]<=MAXN;++j){
    			vis[i*p[j]]=1;
    			if(Mod(i,p[j])==0)break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	for(;T;T--){
    		scanf("%d%d",&N,&mod);
    		printf("%d\n",solve(N));
    	}
    	return 0;
    }
    

    update on 2020.9.8:

    经由大佬 @C3H5ClO 大佬证明,上面这份代码实际上是O(nlogn)O(n\log n)的。

    最后借用一下 @C3H5ClO 大佬的证明:

    $$f(n)=\sum_{d=1}^n\sum_{e=1}^{\lfloor \frac{n}{d} \rfloor}\mu(e)(\sum_{i=1}^{\lfloor \frac{n}{de}\rfloor}d^{de})^2 $$$$T(n)=O(\sum_{d=1}^n\sum_{e=1}^{\lfloor \frac{n}{d} \rfloor}\log\lfloor \frac{n}{de}\rfloor) $$$$T(n)=O(\sum_{i=1}^n\int_0^{\frac{n}{i}}\log\frac{n}{ix}\mathrm{d}x) $$
    $$\int\log\frac{n}{x}\mathrm{d}x=(\log n+1)x-x\log x+C $$0nlognxdx=n\int_0^n\log\frac{n}{x}\mathrm{d}x=n
    T(n)=O(i=1nni)=O(nlnn)T(n)=O(\sum_{i=1}^n\frac{n}{i})=O(n\ln n)

    出这题的本意其实是想看看有没有吊打std\text{std}的做法的,笔者推了很久并没有找到线性的做法。

    • 1

    信息

    ID
    5750
    时间
    1000~6000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者