SDOI2017 数字表格

本文介绍了一种基于数学算法的优化技巧,通过调整变量枚举顺序和引入调和级数预处理,将复杂度从O(n)降低到O(√n),适用于解决特定类型的数学问题。

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

传送门
f [ 0 ] = 0 , f [ 1 ] = 1 , f [ i ] = f [ i − 1 ] + f [ i − 2 ] ( i ⩾ 2 ) f[0]=0,f[1]=1,f[i]=f[i-1]+f[i-2](i \geqslant2) f[0]=0,f[1]=1,f[i]=f[i1]+f[i2](i2)
a n s = ∏ i = 1 n ∏ j = 1 m f [ g c d ( i , j ) ] ans=\prod_{i=1}^{n} \prod_{j=1}^{m} {f[gcd(i,j)]} ans=i=1nj=1mf[gcd(i,j)]
a n s = ∏ i = 1 n ∏ j = 1 m ∏ t = 1 m i n ( n , m ) [ g c d ( i , j ) = = t ] f [ t ] ans=\prod_{i=1}^{n} \prod_{j=1}^{m} \prod_{t=1}^{min(n,m)} {[gcd(i,j)==t]f[t]} ans=i=1nj=1mt=1min(n,m)[gcd(i,j)==t]f[t]
a n s = ∏ t = 1 m i n ( n , m ) f [ t ] ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = t ] ans=\prod_{t=1}^{min(n,m)} f[t]^{\sum_{i=1}^{n} \sum_{j=1}^{m} {[gcd(i,j)==t]}} ans=t=1min(n,m)f[t]i=1nj=1m[gcd(i,j)==t]
令指数为 p [ t ] p[t] p[t]
p [ t ] = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = t ] p[t]={\sum_{i=1}^{n} \sum_{j=1}^{m} {[gcd(i,j)==t]}} p[t]=i=1nj=1m[gcd(i,j)==t]
= ∑ i = 1 ⌊ n t ⌋ ∑ j = 1 ⌊ m t ⌋ [ g c d ( i , j ) = = 1 ] ={\sum_{i=1}^{\lfloor \frac{n}{t}\rfloor} \sum_{j=1}^{\lfloor \frac{m}{t}\rfloor} {[gcd(i,j)==1]}} =i=1tnj=1tm[gcd(i,j)==1]
= ∑ i = 1 ⌊ n t ⌋ ∑ j = 1 ⌊ m t ⌋ ∑ d ∣ i , d ∣ j μ ( d ) ={\sum_{i=1}^{\lfloor \frac{n}{t}\rfloor} \sum_{j=1}^{\lfloor \frac{m}{t}\rfloor} \sum_{d|i,d|j}{\mu(d)}} =i=1tnj=1tmdi,djμ(d)
= ∑ d = 1 μ ( d ) ∗ ⌊ n d ∗ t ⌋ ⌊ m d ∗ t ⌋ =\sum_{d=1}{\mu(d)*\lfloor \frac{n}{d*t}\rfloor \lfloor \frac{m}{d*t}\rfloor} =d=1μ(d)dtndtm
这时候,一次询问需要: O ( n ) O(n) O(n)枚举 t t t,每次再 O ( n t ) O(\sqrt{\frac{n}{t}}) O(tn )求和。
一个优化套路:令 T = d ∗ t T=d*t T=dt,于是:
p [ t ] = ∑ t ∣ T , T ∈ [ 1 , n ] μ ( T t ) ∗ ⌊ n T ⌋ ⌊ m T ⌋ p[t]=\sum_{t|T,T\in[1,n]}{\mu(\frac{T}{t})*\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor} p[t]=tT,T[1,n]μ(tT)TnTm
代入答案:
a n s = ∏ t = 1 m i n ( n , m ) f [ t ] ∑ t ∣ T , T ∈ [ 1 , n ] μ ( T t ) ∗ ⌊ n T ⌋ ⌊ m T ⌋ ans=\prod_{t=1}^{min(n,m)} f[t]^{\sum_{t|T,T\in[1,n]}{\mu(\frac{T}{t})*\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor}} ans=t=1min(n,m)f[t]tT,T[1,n]μ(tT)TnTm
交换 T T T t t t的枚举顺序:
a n s = ∏ T = 1 m i n ( n , m ) ∏ t ∣ T f [ t ] μ ( T t ) ∗ ⌊ n T ⌋ ⌊ m T ⌋ ans=\prod_{T=1}^{min(n,m)} \prod_{t|T} f[t]^{\mu(\frac{T}{t})*\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor} ans=T=1min(n,m)tTf[t]μ(tT)TnTm
a n s = ∏ T = 1 m i n ( n , m ) ( ∏ t ∣ T f [ t ] μ ( T t ) ) ⌊ n T ⌋ ⌊ m T ⌋ ans=\prod_{T=1}^{min(n,m)} (\prod_{t|T} f[t]^{\mu(\frac{T}{t})})^{\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor} ans=T=1min(n,m)(tTf[t]μ(tT))TnTm
R [ T ] = ∏ t ∣ T f [ t ] μ ( T t ) R[T]=\prod_{t|T} f[t]^{\mu(\frac{T}{t})} R[T]=tTf[t]μ(tT),这个东西可以调和级数预处理。
处理完过后,一次询问就变为了 O ( n ) O(\sqrt{n}) O(n )。于是就做完了。
注意一下可以优化的细节。

#include<bits/stdc++.h>
#define re register
#define cs const
cs int N=1e6+10,mod=1e9+7;
int T,n,m,iR[N],R[N],iF[N],F[N];

inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int quickpow(int a,int b,int ret=1){
	if(b<0) b+=mod-1;
	for(;b;b>>=1,a=mul(a,a))
		if(b&1)ret=mul(ret,a);
	return ret;
}
inline int inv(int x){return quickpow(x,mod-2);}
int mark[N],P[N],mu[N],cnt=0;
inline void linear_sieves(){
	mark[0]=mark[1]=1,mu[1]=1;
	for(int re i=2;i<N;++i){
		if(!mark[i]) P[++cnt]=i,mu[i]=-1;
		for(int re j=1;j<=cnt&&i*P[j]<N;++j){
			mark[i*P[j]]=1;
			if(i%P[j]) mu[i*P[j]]=-mu[i];
			else{mu[i*P[j]]=0;break;}
		}
	}
}

inline void prework(){
	F[0]=0,iF[1]=F[1]=1,iR[0]=iR[1]=R[0]=R[1]=1;
	for(int re i=2;i<N;++i) R[i]=1,iF[i]=quickpow(F[i]=add(F[i-1],F[i-2]),mod-2);
	for(int re d=3;d<N;++d)
		for(int re T=d,t=1;T<N;T+=d,++t)
			if(mu[t]!=0) R[T]=mul(R[T],mu[t]==1?F[d]:iF[d]);
	for(int re i=2;i<N;++i) R[i]=mul(R[i-1],R[i]),iR[i]=inv(R[i]);
}

inline int query(int n,int m,int ret=1){
	for(int re i=1,j;i<=std::min(n,m);i=j+1){
		j=std::min(n/(n/i),m/(m/i));
		ret=mul(ret,quickpow(mul(R[j],iR[i-1]),1ll*(n/i)*(m/i)%(mod-1)));
	}return ret;
}
int main(){
	linear_sieves(),prework(),scanf("%d",&T);
	while(T--) scanf("%d%d",&n,&m),printf("%d\n",query(n,m));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值