【CQOI2017】小Q的表格(莫比乌斯反演)(分块)

本文深入解析了一道数学算法竞赛题目,通过将问题转化为数学表达式,利用数学性质和算法技巧,实现了高效的求解策略。文章详细阐述了如何通过观察题目特性,引入辅助函数,运用分块技术和预处理方法,最终达到O(mn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


题解:

首先很显然地,我们发现题目的限制可以转化为 f ( a , b ) a b = f ( b , a ) a b = f ( a , a + b ) a ( a + b ) \frac{f(a,b)}{ab}=\frac{f(b,a)}{ab}=\frac{f(a,a+b)}{a(a+b)} abf(a,b)=abf(b,a)=a(a+b)f(a,a+b)

进而发现,对于任意 a , b a,b a,b,我们可以得到 g ( g c d ( a , b ) ) = f ( a , b ) a b g(gcd(a,b))=\frac{f(a,b)}{ab} g(gcd(a,b))=abf(a,b),也就是说,所有 g c d gcd gcd相同的数对是捆绑在一起的。

那么我们现在需要求的就是

A n s = ∑ i = 1 n ∑ j = 1 n i j ⋅ g ( g c d ( i , j ) ) = ∑ k = 1 n g ( k ) k 2 ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ n k ⌋ i ⋅ j [ g c d ( i , j ) = 1 ] = ∑ k = 1 n g ( k ) k 2 ∑ i = 1 ⌊ n k ⌋ i 2 ϕ ( i ) \begin{aligned} Ans=&\sum_{i=1}^n\sum_{j=1}^nij\cdot g(gcd(i,j))\\ =&\sum_{k=1}^ng(k)k^2\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{k}\rfloor}i\cdot j[gcd(i,j)=1]\\ =&\sum_{k=1}^ng(k)k^2\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}i^2\phi(i) \end{aligned} Ans===i=1nj=1nijg(gcd(i,j))k=1ng(k)k2i=1knj=1knij[gcd(i,j)=1]k=1ng(k)k2i=1kni2ϕ(i)

发现总共有 O ( m ) O(m) O(m)个修改和 O ( m n ) O(m\sqrt n) O(mn )个查询。

用分块来修改就可以做到严格的 O ( m n ) O(m\sqrt n) O(mn )加上预处理的 O ( n ) O(n) O(n),可以轻松通过本题。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

int n,m;

cs int N=4e6+7;

int p[N],pc;
bool mark[N];
int phi[N],f[N];

inline void linear_sieves(){
	phi[1]=1;f[1]=1;
	for(int re i=2;i<=n;++i){
		if(!mark[i])p[++pc]=i,phi[i]=i-1;
		for(int re j=1;i*p[j]<=n;++j){
			mark[i*p[j]]=true;
			if(i%p[j])phi[i*p[j]]=phi[i]*(p[j]-1);
			else {
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
		}
		f[i]=add(f[i-1],mul(mul(i,i),phi[i]));
	}
}

cs int B=2e4+7;

int bsiz,bcnt;
int bl[N],pl[B],pr[B],tag[B],val[N],now[N];

inline int query(int i){
	return add(val[i],tag[bl[i]]);
}

signed main(){
#ifdef zxyoi
	freopen("table.in","r",stdin);
#endif
	m=gi(),n=gi();linear_sieves();
	bsiz=std::max(0.5*sqrt(n),1.);
	for(int re i=1;i<=n;++i){
		if((i-1)%bsiz==0){
			pr[bcnt]=i-1;
			pl[++bcnt]=i;
		}
		bl[i]=bcnt;
		val[i]=add(val[i-1],now[i]=mul(i,i));
	}pr[bcnt]=n;
	while(m--){
		int a=gi(),b=gi();ll x=gl();int k=gi();
		int g=std::__gcd(a,b);
		int d=(x/(a/g)/(b/g))%mod;
		int ad=dec(d,now[g]);
		now[g]=d;
		for(int re i=g;i<=pr[bl[g]];++i)Inc(val[i],ad);
		for(int re i=bl[g]+1;i<=bcnt;++i)Inc(tag[i],ad);
		int ans=0;
		for(int re l=1,r;l<=k;l=r+1){
			r=k/(k/l);
			Inc(ans,mul(dec(f[r],f[l-1]),query(k/l)));
		}
		cout<<ans<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值