对于「学习记录」不含对知识点的解析,只是对本人写题的一个记录。
-
先不考虑 (a) 的情况,可以列出式子,不妨设 (n<m) 。
[ans= sumlimits_{i=1}^n sumlimits_{j=1}^msigma(gcd(i,j)) ][ans = sumlimits_{d=1}^{n} sumlimits_{i=1}^{leftlfloor frac{n}{d} ight floor} sumlimits_{j=1}^{leftlfloor frac{m}{d} ight floor} sigma(d)[gcd(i,j)=1] ][ans = sumlimits_{d=1}^{n} sigma(d) sumlimits_{i=1}^{leftlfloor frac{n}{d} ight floor} sumlimits_{j=1}^{leftlfloor frac{m}{d} ight floor} sumlimits_{x|i,x|j} mu(x) ][ans = sumlimits_{d=1}^n sigma(d) sumlimits_{x=1}^{ leftlfloor frac{n}{d} ight floor} mu(x) leftlfloor dfrac{n}{dx} ight floor leftlfloor dfrac{m}{dx} ight floor ]令 (t=dx) ,然后替换进去
[ans = sumlimits_{t=1}^n leftlfloor dfrac{n}{t} ight floor leftlfloor dfrac{m}{t} ight floor sumlimits_{d|t} sigma(d) mu ( leftlfloor frac{t}{d} ight floor) ]此时可以整除分块,后一段预处理出来后存一下前缀和 (g_i = sumlimits_{d|i} sigma(d) mu( leftlfloor dfrac{i}{d} ight floor))。
现在考虑有 (a) 的限制,也就是 $ sigma(d) le a $ ,即我们只需要维护 (g_i) 。
我们可以先离线一下询问,并且按照 (a_i) 来从小到大的排序。每次只需要动态对 (g_i) 进行修改,使本组的 (g_i) 满足 $ sigma(d) le a_i $,并在整除分块时查询区间和。此处可以选择树状数组。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll N=1e5+5;
const ll Mod=(1<<31);
struct Query{
ll x,y,z,id;
}q[N],qi[N];
ll T,n,m,cnt,mu[N],d[N],sum[N],pri[N];
ll t[N],ans[N];
bool vis[N];
bool cmp(Query a,Query b){
return a.z<b.z;
}
ll lowbit(ll x){return x&(-x);}
void modify(ll x,ll y){
for(;x<=N-5;x+=lowbit(x))t[x]+=y;
}
ll query(ll x)
{ ll res=0;
for(;x;x-=lowbit(x))res+=t[x];
return res;
}
void init()
{ mu[1]=1;
for(ll i=2;i<=N-5;i++)
{ if(!vis[i])pri[++cnt]=i,mu[i]=-1;
for(ll j=1;j<=cnt&&pri[j]*i<=N-5;j++)
{ vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
mu[i*pri[j]]=-mu[i];
}
}
for(ll i=1;i<=N-5;i++)
for(ll j=i;j<=N-5;j+=i)
d[j]+=i;
for(ll i=1;i<=N-5;i++)
qi[i].id=i,qi[i].z=d[i];
sort(qi+1,qi+1+(N-5),cmp);
}
ll solve(ll x,ll y)
{ ll res=0;
for(ll l=1,r;l<=min(x,y);l=r+1)
{ r=min((x/(x/l)),(y/(y/l)));
res+=(x/l)*(y/l)*(query(r)-query(l-1));
}
return res;
}
int main()
{ //freopen("P3312_2.in","r",stdin);
//freopen("ans.out","w",stdout);
scanf("%lld",&T);
for(ll i=1;i<=T;i++)
{ scanf("%lld%lld%lld",&q[i].x,&q[i].y,&q[i].z);
q[i].id=i;
}
sort(q+1,q+1+T,cmp);
init();
ll k=0;
for(ll i=1;i<=T;i++)
{ while(qi[k+1].z<=q[i].z&&k<N-5)
{ ll x=qi[++k].id;
for(ll j=1;j*x<=N-5;j++)modify(j*x,mu[j]*d[x]);
}
ans[q[i].id]=solve(q[i].x,q[i].y);
}
for(ll i=1;i<=T;i++)
printf("%lld
",ans[i]%Mod);
return 0;
}
-
题意:求 (ans = sumlimits_{i=1}^n sumlimits_{j=1}^m operatorname{lcm}(i,j)) 。
根据题意可以写出式子,不妨设 (n<m) 。
[egin{aligned} ans &= sumlimits_{i=1}^n sumlimits_{j=1}^m operatorname{lcm}(i,j) \ &= sumlimits_{i=1}^n sumlimits_{j=1}^m dfrac{i cdot j}{gcd(i,j)} \ &= sumlimits_{d=1}^n sumlimits_{i=1}^n sumlimits_{j=1}^m dfrac{i cdot j}{d}[gcd(i,j)=d] \ &= sumlimits_{d=1}^n d sumlimits_{i=1}^{leftlfloor dfrac{n}{d} ight floor} sumlimits_{j=1}^{leftlfloor dfrac{m}{d} ight floor} i cdot j [gcd(i,j)=1] \ &= sumlimits_{d=1}^n d sumlimits_{i=1}^{leftlfloor dfrac{n}{d} ight floor} sumlimits_{j=1}^{leftlfloor dfrac{m}{d} ight floor} i cdot j sumlimits_{x|i,x|j} mu(x) end{aligned} ]枚举 (k) 。
[egin{aligned} ans &= sumlimits_{d=1}^{n} d sumlimits_{k=1}^{leftlfloor dfrac{n}{d} ight floor} mu(k) cdot k^2 sumlimits_{i=1}^{leftlfloor dfrac{n}{d cdot k} ight floor} sumlimits_{j=1}^{leftlfloor dfrac{m}{d cdot k} ight floor} i cdot j end{aligned} ]此时,跑整除分块,用等差数列预处理后面式子,复杂度为 $O( sumlimits_{i=1}^{sqrt{n}} sqrt{leftlfloor dfrac{n}{i} ight floor} +sqrt{i} ) =O(int_0^{sqrt{n}} sqrt{x} dx) =O(n^{0.75}) $ 。已经可以通过本题。
但我们考虑继续优化。
设 (s(x)=sumlimits_{i=1}^x i, t=dk) 。替换进去。
[egin{aligned} ans &= sumlimits_{d=1}^n d sumlimits_{k=1}^{leftlfloor dfrac{n}{d} ight floor} mu(k) cdot k^2 cdot s(leftlfloor dfrac{n}{t} ight floor) cdot s(leftlfloor dfrac{m}{t} ight floor) end{aligned} ]枚举 (t) 。
[egin{aligned} ans &= sumlimits_{t=1}^n s(leftlfloor dfrac{n}{t} ight floor) cdot s(leftlfloor dfrac{m}{t} ight floor) sumlimits_{d|t} d cdot mu( dfrac{t}{d} ) cdot (dfrac{t}{d})^2 \ &= sumlimits_{t=1}^n s(leftlfloor dfrac{n}{t} ight floor) cdot s(leftlfloor dfrac{m}{t} ight floor) sumlimits_{d|t} dfrac{t}{d} cdot mu( d ) cdot d^2 \ &= sumlimits_{t=1}^n s(leftlfloor dfrac{n}{t} ight floor) cdot s(leftlfloor dfrac{m}{t} ight floor) sumlimits_{d|t} t cdot mu( d ) cdot d end{aligned} ]不妨设 (f(x)= sumlimits_{d|x} mu(d) cdot d) 。
考虑 (gcd(a,b)=1) 。有 (f(a)= sumlimits_{d|a} mu(d) cdot d,f(b)= sumlimits_{d|b} mu(d) cdot d) 。
因为 (mu) 是积性函数,且 (a) 和 (b) 是互质的,质因子不同。
则在 (gcd(a,b)=1) 时, (f(a cdot b)=f(a) cdot f(b)) ,是积性函数。
现在考虑 (a equiv 0 ( mod b )) ,且 (b) 为质数。则为 (b) 的因数一定为 (a) 的因数,且这几个 (mu(d) cdot d) 结果一定相同。而 (a) 中不为 (b) 的因数,质因数分解后次数一定大于 (1) ,也就是 (mu(d)=0) 。所以可以得出 (f(a)=f(b)) 。
这时已经可以线筛求出 (f(x)) 了。然后前缀和预处理时多乘上一个 (t) 。就可以整除分块直接求了。复杂度 (O(sqrt{n})) 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll N=1e7+5;
const ll Mod=20101009;
ll n,m,cnt,ans,pri[N],mu[N],s[N],f[N];
bool vis[N];
void init()
{ mu[1]=f[1]=1;
for(ll i=2;i<=N-5;i++)
{ if(!vis[i])mu[i]=-1,pri[++cnt]=i,f[i]=(1-i+Mod)%Mod;
for(ll j=1;i*pri[j]<=N-5&&j<=cnt;j++)
{ vis[i*pri[j]]=1;
if(i%pri[j]==0){f[i*pri[j]]=f[i];break;}
mu[i*pri[j]]=-mu[i];
f[i*pri[j]]=f[i]*f[pri[j]]%Mod;
}
}
for(ll i=1;i<=N-5;i++)f[i]=(f[i-1]+f[i]*i%Mod)%Mod;
for(ll i=1;i<=N-5;i++)s[i]=(s[i-1]+i)%Mod;
}
int main()
{ init();
scanf("%lld%lld",&n,&m);
for(ll l=1,r;l<=min(n,m);l=r+1)
{ r=min(n/(n/l),m/(m/l));
ans=(ans+(f[r]-f[l-1]+Mod)%Mod*s[n/l]%Mod*s[m/l]%Mod)%Mod;
}
printf("%lld
",ans);
return 0;
}
-
题意:求 (sumlimits_{i=1}^n sumlimits_{j=1}^n i cdot j cdot gcd(i,j)) 。
本题和[国家集训队]Crash的数字表格相似,
或者说几乎一样,这里不再赘述。其实就是懒。[ans= sumlimits_{t=1}^n s( leftlfloor dfrac{n}{t} ight floor)^2 sumlimits_{d|t} t^2 cdot d cdot mu(leftlfloor dfrac{t}{d} ight floor) ]其中 (s(x)=sumlimits_{i=1}^x i) 。
但数据范围是 (n le 10^{10}) ,所以 (s(x)) 直接用 (dfrac{n imes (n+1)}{2}) 。
而 (sumlimits_{d|t} t^2 cdot d cdot mu(leftlfloor dfrac{t}{d} ight floor)) ,考虑用杜教筛处理。
因为 ((id* mu )(i)=varphi(i)) 。即 (sumlimits_{d|t} t^2 cdot d cdot mu(leftlfloor dfrac{t}{d} ight floor) = t^2 varphi(t)) 。令 (f(i)=i^2 varphi(i)) ,求 (S(n)=sumlimits_{i=1}^n f(n)) 。
杜教筛:
[g(1)S(n)=sumlimits_{i=1}^n (g* f)(i)-sumlimits _ {i=2}^n g(i)S(dfrac{n}{i}) ]取 (g(x)=x^2) 。则:
[S(n)=sumlimits _ {i=1}^n (g*f)(i)-sumlimits_{i=2}^n g(i)S(dfrac{n}{i}) ]因为 (sumlimits_{d|n} varphi(d) =n) 。则:
[(g * f)(i)= sumlimits_{d|i} f(d)g(dfrac{i}{d}) = sumlimits_{d|i} d^2 varphi(i) (dfrac{i}{d})^2 = i^2 sumlimits_{d|i} varphi(i) = i^3 ][S(n)= sumlimits_{i=1}^n i^3 -sumlimits_{i=2}^n i^2 S(dfrac{n}{i}) ]因为 (sumlimits_{i=1}^n i^3 = (sumlimits_{i=1}^n i)^2 = dfrac{n(n+1)(2n+1)}{6}) ,为 (s(n)^2) 。
此时 (S(n)) 已经可以用杜教筛算出。前面用整除分块即可,复杂度 (O(n^{frac{2}{3}} + sqrt{n})) 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
const ll N=8e6+5;
ll n,Mod,ans,inv6,inv2,cnt,pri[N],pi[N];
bool vis[N];
map<ll,ll> mp;
ll qpow(ll x,ll k)
{ ll res=1;
while(k)
{ if(k&1)res=res*x%Mod;
x=x*x%Mod;
k>>=1;
}
return res;
}
void init()
{ vis[1]=1;pi[1]=1;
for(ll i=2;i<=N-5;i++)
{ if(!vis[i])pri[++cnt]=i,pi[i]=i-1;
for(ll j=1;j<=cnt&&i*pri[j]<=N-5;j++)
{ vis[i*pri[j]]=1;
if(i%pri[j])pi[i*pri[j]]=1ll*pi[i]*pi[pri[j]]%Mod;
else {pi[i*pri[j]]=pi[i]*pri[j]%Mod;break;}
}
}
for(ll i=1;i<=N-5;i++)pi[i]=(pi[i-1]+pi[i]*i%Mod*i%Mod)%Mod;
}
ll getsum(ll x)
{ x%=Mod;
return x*(x+1)%Mod*inv2%Mod;
}
ll getsump(ll x)
{ x%=Mod;
return x*(x+1)%Mod*(x+x+1)%Mod*inv6%Mod;
}
ll getpi(ll x)
{ if(x<=N-5)return pi[x];
if(mp[x])return mp[x];
ll res=getsum(x);
res=res*res%Mod;
for(ll l=2,r;l<=x;l=r+1)
{ r=x/(x/l);
ll t=(getsump(r)-getsump(l-1))%Mod;
res=(res-getpi(x/l)*t%Mod+Mod)%Mod;
}
return mp[x]=res;
}
int main()
{ scanf("%lld%lld",&Mod,&n);
inv2=qpow(2,Mod-2);inv6=qpow(6,Mod-2);
init();
for(ll l=1,r;l<=n;l=r+1)
{ r=n/(n/l);
ll t=getsum(n/l),g=(getpi(r)-getpi(l-1))%Mod;
t=t*t%Mod;g=(g+Mod)%Mod;
ans=(ans+g*t%Mod)%Mod;
}
printf("%lld
",ans);
return 0;
}
-
题意:定义 (f(0)=f(1)=1,f(n)=f(n-1)+f(n-2)) ,求:
[prodlimits_{i=1}^n prodlimits_{j=1}^m f(gcd(i,j)) ]不妨设 (n<m) 。
[egin{aligned} ans &= prodlimits_{d=1}^n prodlimits_{i=1}^n prodlimits_{j=1}^m f(d)[gcd(i,j)=d] \ &= prodlimits_{d=1}^n f(d)^{sumlimits_{i=1}^{leftlfloor frac{n}{d} ight floor}sumlimits_{j=1}^{leftlfloor frac{m}{d} ight floor} [gcd(i,j)=1]} end{aligned} ]因为写的太丑了,所以单独拎出指数。
[egin{aligned} sumlimits_{i=1}^{leftlfloor frac{n}{d} ight floor}sumlimits_{j=1}^{leftlfloor frac{m}{d} ight floor} [gcd(i,j)=1]=sumlimits_{k=1}^{leftlfloor frac{n}{d} ight floor} mu(k) leftlfloor frac{n}{dk} ight floor leftlfloor frac{m}{dk} ight floor end{aligned} ]设 (t=dk) 。
[egin{aligned} sumlimits_{k=1}^{leftlfloor frac{n}{d} ight floor} mu(leftlfloor frac{n}{d} ight floor) leftlfloor frac{n}{t} ight floor leftlfloor frac{m}{t} ight floor end{aligned} ][egin{aligned} ans &=prodlimits_{d=1}^n f(d)^{sumlimits_{k=1}^{leftlfloor frac{n}{d} ight floor} mu(leftlfloor frac{n}{d} ight floor) leftlfloor frac{n}{t} ight floor leftlfloor frac{m}{t} ight floor} \ &= prodlimits _{t=1}^n prod_{d|t} f(d)^{mu(leftlfloor frac{n}{d} ight floor) leftlfloor frac{n}{t} ight floor leftlfloor frac{m}{t} ight floor} \ &= prodlimits _{t=1}^n prod_{d|t} left( f(d)^{mu(leftlfloor frac{n}{d} ight floor) } ight) ^{leftlfloor frac{n}{t} ight floor leftlfloor frac{m}{t} ight floor} end{aligned} ]此时我们可以对第一个 (prod) 进行整除分块。那里面呢?
考虑暴力算,复杂度 (O(n)) 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll N=1e6+5;
const ll Mod=1e9+7;
ll T,n,m,cnt,ans,inv,pri[N],mu[N],f[N],g[N],F[N];
bool vis[N];
ll qpow(ll x,ll k)
{ ll res=1;
while(k)
{ if(k&1)res=res*x%Mod;
x=x*x%Mod;
k>>=1;
}
return res;
}
void init()
{ f[1]=g[1]=F[0]=F[1]=mu[1]=1;
vis[1]=1;
for(ll i=2;i<=N-5;i++)
{ f[i]=(f[i-1]+f[i-2])%Mod;
g[i]=qpow(f[i],Mod-2);F[i]=1;
if(!vis[i])pri[++cnt]=i,mu[i]=-1;
for(ll j=1;j<=cnt&&pri[j]*i<=N-5;j++)
{ vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
mu[i*pri[j]]=-mu[i];
}
}
for(ll i=1;i<=N-5;i++)
{ if(!mu[i])continue;
for(ll j=i;j<=N-5;j+=i)
F[j]=F[j]*(mu[i]==1?f[j/i]:g[j/i])%Mod;
}
for(ll i=2;i<=N-5;i++)F[i]=F[i]*F[i-1]%Mod;
}
int main()
{ init();
scanf("%lld",&T);
while(T--)
{ scanf("%lld%lld",&n,&m);
ans=1ll;inv=1ll;
for(ll l=1,r;l<=min(n,m);l=r+1)
{ r=min(n/(n/l),m/(m/l));
inv=F[r]*qpow(F[l-1],Mod-2)%Mod;
ans=ans*qpow(inv,(n/l)*(m/l))%Mod;
}
printf("%lld
",ans);
}
return 0;
}
-
DIVCNT2 - Counting Divisors (square)
题意:求 (sumlimits_{i=1}^n d(i^2)) ,其中 (d(x)) 表示 (x) 的约数个数。
约定,$mathbf{1}(n)=mathbf{id}^0(n)=1 $ 。
不妨设 (n=prodlimits_{i=1}^k p_i^{a_i}) ,则 (n^2=prodlimits_{i=1}^k p_i^{2a_i}) 。
[egin{aligned} d(i^2) &= sumlimits_{u|i} sumlimits_{v|i} [gcd(u,v)=1] \ &=sumlimits_{x|i} sumlimits_{u|x} [gcd(u,dfrac{x}{u})=1] end{aligned} ]这一部分可以理解为,一个因数 (x) 的质因数要么给 (u) ,要么不给 (u) (若不全给即不影响结果)。所以一个质因数就有两种结果,即 (2^{omega(x)}) ,其中 (x) 表示质因数个数。
[egin{aligned} d(i^2) &= sumlimits_{s subseteq {1,2,...,k}} 2^{|s|} imes prodlimits_{i in s} a_i \ &=sumlimits _{d|n} mu ^2(d) end{aligned} ]设 (f(n)= d(n^2),g(n)=2^ {omega(n)},h(n)=mu^2(n)) ,则 $ f = g * mathbf{1},g=h*mathbf{1}$ 。
所以 (f=h * mathbf{1} * mathbf{1} = h * (mathbf{1} * mathbf{1})=h * d = mu ^2(n)*d) 。本题即求 (f) 的前缀和。
也就是说我们要预处理 (mu^2(n)) 和 (d) 。首先 (d) 很显然:
[sumlimits_{i=1}^nd(i)=sumlimits_{i=1}^n leftlfloor dfrac{n}{i} ight floor ]整除分块即可,复杂度 (mathcal{O}(sqrt{n})) 。
考虑 (mu^2(n)) ,不妨设 (s(n)) 为满足 (k^2|n) 的最大 (k) 。所以 (d|s(n) Leftrightarrow d^2 | n) ,继续推导:
[mu^2(n) = [s(n)=1] = sumlimits_{d|s(n)} mu(d) = sumlimits_{d^2 | n}mu(d) ]于是
[sumlimits_{i=1}^n mu^2(i) = sumlimits_{i=1}^n sumlimits_{d^2 | i} mu(d)= sumlimits_{i=1} ^ {sqrt{n}} mu(i) cdot leftlfloor dfrac{n}{i^2} ight floor ]这样就可以 (mathcal{O}(sqrt{n})) 算了。整除分块套整除分块,考虑预处理 (n^{frac{2}{3}}) 的前缀和。 复杂度 (mathcal{O}(n^{frac{2}{3}})) ,可以通过本题。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+5,M=1e8+5;
ll n,mx,B,s[M],a[N];
int cnt,pri[M],smu[M];
char mu[M];
bool vis[M];
void init(int m)
{ s[1]=1;smu[1]=1;mu[1]=1;
for(int i=2;i<=m;i++)
{ if(!vis[i])pri[++cnt]=i,mu[i]=-1,smu[i]=1,s[i]=2;
for(int j=1;j<=cnt&&i*pri[j]<=m;j++)
{ vis[i*pri[j]]=1;
if(i%pri[j]==0)
{ s[i*pri[j]]=s[i]/(smu[i]+1)*(smu[i]+2);
smu[i*pri[j]]=smu[i]+1;
mu[i*pri[j]]=0;
break;
}
s[i*pri[j]]=s[i]*2;
smu[i*pri[j]]=1;
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=m;i++)
{ s[i]+=s[i-1];
smu[i]=smu[i-1]+abs((int)mu[i]);
}
}
ll getr(ll x)
{ if(x<=B)return s[x];
ll res=0;
for(ll l=1,r;l<=x;l=r+1)
{ r=(x/(x/l));
res+=(r-l+1)*(x/l);
}
return res;
}
ll getsmu(ll x)
{ if(x<=B)return smu[x];
ll res=0;
for(ll i=1;i*i<=x;i++)
if(mu[i])res+=mu[i]*(x/(i*i));
return res;
}
void solve(ll nn)
{ ll m=sqrt(nn),pre=smu[m],tt,ans=0;
for(int i=1;i<=m;i++)
if(mu[i])ans+=getr(nn/i);
for(ll l=m+1,r;l<=nn;l=r+1)
{ r=(nn/(nn/l));
tt=getsmu(r);
ans+=(tt-pre)*getr(nn/l);
pre=tt;
}
printf("%lld
",ans);
}
int main()
{ scanf("%lld",&n);
for(int i=1;i<=n;i++)
{ scanf("%lld",&a[i]);
mx=max(mx,a[i]);
}
if(mx<=10000)B=10000;
else B=pow(1000000000000,2.0/3.0);
init(B);
for(int i=1;i<=n;i++)
solve(a[i]);
return 0;
}