1 条题解
-
0
自动搬运
来自洛谷,原作者为

Refined_heart
再见。搬运于
2025-08-24 22:24:22,当前版本为作者最后更新于2020-09-08 18:41:31,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
官方题解update on 2020/9/9 更新了复杂度证明。
我太菜了……题解改了好多次,麻烦管理了qwq对于百分之十的数据:随便过。
下面推式子:
$$=\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)} $$令
$$=\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)=\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} $$第三行就是两行相减。
对第一行的解释:这里的数,每个数作为都出现了次。因为可以取遍后面的那一些,会发现最大只能到不能再取遍个值了。此时能取到的应该是种。
对于这里是把第一个式子的最后一个值移动到了后面那个式子,方便做差。
这时我们可以在小模数的情况下做到)的预处理。
- 化简形式
令
则原式为$\sum_{i=1}^\frac{n}{T}\sum_{j=1}^\frac{n}{T} x^{i+j}.$
像不像一个多项式。
它就等于
于是我们可以等比数列求和解出。
剩下的,可以做到处理出整个式子。
#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; }由于这里是的数据,所以略微卡常,但笔者通过非常不精湛的卡常技术跑到了以内,所以这里的时间限制我开了.
对等比数列进行精细处理,可以做到的复杂度。
#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; }由于常数等原因,这分代码可以拿到分的好成绩。但我们可以通过另一种做法将常数/复杂度降低。
另一种做法
观察:
$$\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. $$这里同样观察式子发现可以直接算。前一部分是的倍调和级数的复杂度,后面带上一个精细处理的等比数列求求和复杂度。
(代码中的优化即使不加也是可以过的)
#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 大佬证明,上面这份代码实际上是的。
最后借用一下 @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 $$
出这题的本意其实是想看看有没有吊打的做法的,笔者推了很久并没有找到线性的做法。
- 1
信息
- ID
- 5750
- 时间
- 1000~6000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者