洛谷P3312 [SDOI2014]数表

文章讲述了如何使用莫比乌斯反演解决洛谷P3312题目中的数表问题,涉及到数学和算法的知识,包括数论分块、前缀和以及树状数组的动态维护,主要讨论了如何在限制条件a下优化计算过程,以达到较高的时间复杂度效率。

洛谷P3312 [SDOI2014]数表

前置知识:莫比乌斯反演

题解

不妨设 n ≤ m n\leq m nm
若没有 a a a的限制,则题意即求

∑ i = 1 n ∑ j = 1 n σ ( gcd ⁡ ( i , j ) ) \sum\limits_{i=1}^n\sum\limits_{j=1}^n\sigma(\gcd(i,j)) i=1nj=1nσ(gcd(i,j))

枚举 gcd ⁡ \gcd gcd可得

∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ σ ( d ) [ gcd ⁡ ( i , j ) = 1 ] \sum\limits_{d=1}^n\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sigma(d)[\gcd(i,j)=1] d=1ni=1dnj=1dmσ(d)[gcd(i,j)=1]

σ ( d ) \sigma(d) σ(d)移到前面,利用莫比乌斯函数的性质,可得

∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sum\limits_{k|i,k|j}\mu(k) d=1nσ(d)i=1dnj=1dmki,kjμ(k)

稍作整理可得

∑ d = 1 n σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ i , k ∣ j μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sum\limits_{k|i,k|j}\mu(k) d=1nσ(d)i=1dnj=1dmki,kjμ(k)

枚举 k k k,可得

∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ ∑ i = 1 ⌊ n k d ⌋ ∑ j = 1 ⌊ m k d ⌋ μ ( k ) \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\sum\limits_{i=1}^{\lfloor\frac{n}{kd}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{kd}\rfloor}\mu(k) d=1nσ(d)k=1dni=1kdnj=1kdmμ(k)

即可得

∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n k d ⌋ ⌊ m k d ⌋ \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\mu(k)\lfloor\dfrac{n}{kd}\rfloor\lfloor\dfrac{m}{kd}\rfloor d=1nσ(d)k=1dnμ(k)kdnkdm

T = k d T=kd T=kd,得

∑ d = 1 n σ ( d ) ∑ k = 1 ⌊ n d ⌋ μ ( k ) ⌊ n T ⌋ ⌊ m T ⌋ \sum\limits_{d=1}^n\sigma(d)\sum\limits_{k=1}^{\lfloor\frac nd\rfloor}\mu(k)\lfloor\dfrac nT\rfloor\lfloor\dfrac mT\rfloor d=1nσ(d)k=1dnμ(k)TnTm

枚举 T T T可得

∑ T = 1 n ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T σ ( d ) μ ( T d ) \sum\limits_{T=1}^n\lfloor\dfrac nT\rfloor\lfloor\dfrac mT\rfloor\sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) T=1nTnTmdTσ(d)μ(dT)

如果没有 a a a的限制,则只需求出 ∑ d ∣ T σ ( d ) μ ( T d ) \sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) dTσ(d)μ(dT)的前缀和,再用数论分块就可以了。

f ( T ) = ∑ d ∣ T σ ( d ) μ ( T d ) f(T)=\sum\limits_{d|T}\sigma(d)\mu(\dfrac Td) f(T)=dTσ(d)μ(dT),我们发现只有当 σ ( d ) ≤ a \sigma(d)\leq a σ(d)a时, σ ( d ) × μ ( T d ) \sigma(d)\times \mu(\dfrac Td) σ(d)×μ(dT)才会对 f ( T ) f(T) f(T)有贡献。

我们可以按 σ ( d ) \sigma(d) σ(d)从小到大来对 d d d排序,对询问按 a a a从小到大排序。随着 a a a的增大,会有一些 σ ( d ) × μ ( T d ) \sigma(d)\times \mu(\dfrac Td) σ(d)×μ(dT) f ( T ) f(T) f(T)从无贡献变为有贡献。可以用树状数组维护 f ( T ) f(T) f(T),然后动态修改。原来的式子用数论分块来求,求 f ( T ) f(T) f(T)的前缀和可以用区间查询来实现。

更新 f ( T ) f(T) f(T)最多需要 ∑ i = 1 n n i ≈ n ln ⁡ n \sum\limits_{i=1}^n\dfrac ni \approx n\ln n i=1ninnlnn次,每次更新 f ( T ) f(T) f(T)的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),则更新的时间复杂度为 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)。每次处理询问的时间复杂度为 O ( n log ⁡ n ) O(\sqrt n\log n) O(n logn),则处理询问的时间复杂度为 O ( q n log ⁡ n ) O(q\sqrt n\log n) O(qn logn)。总时间复杂度为 O ( q n log ⁡ n + n log ⁡ 2 n ) O(q\sqrt n\log n+n\log^2 n) O(qn logn+nlog2n)

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
const long long mod=2147483648ll;
int t,z[100005],p[100005],mu[100005],ans[20005],tr[100005];
long long x;
struct node1{
	int x,id;
}g[100005];
struct node2{
	int n,m,a,id;
}w[20005];
bool cmp1(node1 ax,node1 bx){
	return ax.x<bx.x;
}
bool cmp2(node2 ax,node2 bx){
	return ax.a<bx.a;
}
int lb(int i){
	return i&(-i);
}
void pt(int i,int t){
	while(i<=N){
		tr[i]+=t;i+=lb(i);
	}
}
int gt(int i){
	int re=0;
	while(i){
		re+=tr[i];i-=lb(i);
	}
	return re;
}
int sum(int n,int m){
	int re=0;
	for(int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		re+=(gt(r)-gt(l-1))*(n/l)*(m/l);
	}
	return re;
}
int main()
{
	mu[1]=1;
	for(int i=2;i<=N;i++){
		if(!z[i]){
			p[++p[0]]=i;mu[i]=-1;
		}
		for(int j=1;j<=p[0]&&i*p[j]<=N;j++){
			z[i*p[j]]=1;
			if(i%p[j]==0){
				mu[i*p[j]]=0;
				break;
			}
			mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<=N;i++){
		for(int j=i;j<=N;j+=i) g[j].x+=i;
	}
	for(int i=1;i<=N;i++) g[i].id=i;
	sort(g+1,g+N+1,cmp1);
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
		scanf("%d%d%d",&w[i].n,&w[i].m,&w[i].a);w[i].id=i;
		if(w[i].n>w[i].m) swap(w[i].n,w[i].m);
	}
	sort(w+1,w+t+1,cmp2);
	for(int i=1,j=0;i<=t;i++){
		while(w[i].a>=g[j+1].x&&j+1<=N){
			++j;
			for(int k=g[j].id;k<=N;k+=g[j].id){
				pt(k,g[j].x*mu[k/g[j].id]);
			}
		}
		ans[w[i].id]=sum(w[i].n,w[i].m);
	}
	for(int i=1;i<=t;i++){
	ans[i]=(1ll*ans[i]+mod)%mod;
		printf("%lld\n",ans[i]);
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值