【HDU6417】Rikka with APSP(min_25筛)

传送门


题解:

考虑怎么算出 a a a b b b 的最短路。

S a , b S_{a,b} Sa,b 表示 a a a b b b 的唯一表示中指数奇偶性不一样的质数集合。

d a , b = { 1 ∣ S a , b ∣ = 0 ∏ p ∈ S a , b p ∣ S a , b ∣ > 0 d_{a,b}=\left\{\begin{aligned}1 &&|S_{a,b}|=0\\ \prod_{p\in S_{a,b}}p&&| S_{a,b}|>0\end{aligned}\right. da,b=1pSa,bpSa,b=0Sa,b>0

d i s a , b dis_{a,b} disa,b 表示 a , b a,b a,b之间的最短路,容易发现:

d i s a , b = { 1 ∣ S a , b ∣ = 0 ∑ p ∈ S a , b p ∣ S a , b ∣ > 0 dis_{a,b}=\left\{ \begin{aligned} 1 &&|S_{a,b}|=0\\ \sum_{p\in S_{a,b}}p&&|S_{a,b}|>0 \end{aligned} \right. disa,b=1pSa,bpSa,b=0Sa,b>0

于是很显然地,我们需要分两部分计算。

先考虑 ∣ S a , b ∣ = 0 |S_{a,b}|=0 Sa,b=0 的部分,也就是说 a , b a,b a,b 所有质因子指数的奇偶性都相等。

把指数为奇数的质因子拿出来,设这些质因子乘积为 c c c ,那么 a / c , b / c a/c,b/c a/c,b/c 就是两个不同的完全平方数,其平方根在 [ 1 , ⌊ n c ⌋ ] [1,\lfloor\sqrt\frac{n}{c}\rfloor] [1,cn ] 当中。而所有合法的 c c c 其实就是所有无平方因子数。

这一部分的答案很容易写出来,就是 ∑ i = 1 n μ 2 ( i ) ( ⌊ n i ⌋ 2 ) \sum_{i=1}^n\mu^2(i){\lfloor\sqrt\frac{n}{i}\rfloor\choose 2} i=1nμ2(i)(2in )

后面可以整除分块,所以考虑处理前面的 μ 2 \mu^2 μ2 前缀和:

∑ i = 1 n μ 2 ( i ) = ∑ i = 1 n ∑ d 2 ∣ i μ ( d ) = ∑ d = 1 n μ ( d ) ⌊ n d 2 ⌋ \sum_{i=1}^n\mu^2(i)=\sum_{i=1}^n\sum_{d^2\mid i}\mu(d)=\sum_{d=1}^{\sqrt n}\mu(d)\lfloor\frac{n}{d^2}\rfloor i=1nμ2(i)=i=1nd2iμ(d)=d=1n μ(d)d2n

线筛预处理一部分,后面的暴力计算,复杂度可以做到杜教筛那样。

第二部分由于是求和形式,所以直接对于每个质数,考虑有多少对会用到它就行了。

显然只要一个数的指数是奇数,另一个是偶数就行了。

c a l c ( n , p ) calc(n,p) calc(n,p) 表示 [ 1 , n ] [1,n] [1,n] 中有多少数 p p p 的指数为奇数,显然有关系式:

c a l c ( n , p ) = { ⌊ n / p ⌋ − c a l c ( ⌊ n / p ⌋ , p ) n ≥ p 2 ⌊ n / p ⌋ n < p 2 calc(n,p)=\left\{ \begin{aligned} &\lfloor n/p\rfloor-calc(\lfloor n/p\rfloor,p) &&n\geq p^2\\ &\lfloor n/p\rfloor &&n < p^2 \end{aligned} \right. calc(n,p)={n/pcalc(n/p,p)n/pnp2n<p2

而答案显然为

∑ p p ⋅ c a l c ( n , p ) ⋅ ( n − c a l c ( n , p ) ) \sum_{p}p\cdot calc(n,p)\cdot (n-calc(n,p)) ppcalc(n,p)(ncalc(n,p))

对于大于 n \sqrt n n 的,min_25筛 处理一下区间素数之和即可,小于 n \sqrt n n 的质数暴力枚举即可。

复杂度瓶颈在于处理区间素数之和,为 O ( n 3 / 4 / log ⁡ n ) O(n^{3/4}/\log n) O(n3/4/logn)


代码:

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

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

cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
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);}
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}inline int inv(int a){int x,y;ex_gcd(mod,a,y,x);return x+(x>>31&mod);}
inline int MD(ll x){return x>=mod?x%mod:x;}

cs int N=1e6+7;

int p[N],pc;
int mu[N],sm[N];
bool mrk[N];

void linear_sieves(){
	sm[1]=1;mu[1]=1;
	for(int re i=2;i<N;++i){
		if(!mrk[i])p[++pc]=i,mu[i]=-1;
		sm[i]=sm[i-1]+!!mu[i];
		for(int re j=1;i*p[j]<N;++j){
			mrk[i*p[j]]=true;
			if(i%p[j]==0)break;
			mu[i*p[j]]=-mu[i];
		}
	}
}
int S(ll n){
	if(n<N)return sm[n];
	int ans=0,nn=sqrt(n);
	for(int re d=1;d<=nn;++d)if(mu[d])
		mu[d]==1?Inc(ans,MD(n/d/d)):Dec(ans,MD(n/d/d));
	return ans;
}

inline int Sum(ll x){
	x=MD(x);return MD(x*(x+1)>>1);
}
inline int C(ll x){
	x=MD(x);return MD(x*(x-1)>>1);
}

ll n;int nn;
int f1[N],f2[N];

void min_25_sieve(){
	nn=sqrt(n);
	for(int re i=1;i<=nn;++i)
		f1[i]=dec(Sum(i),1),f2[i]=dec(Sum(n/i),1);
	for(int re i=1;p[i]<=nn;++i){
		int p=::p[i],_f=f1[p-1];
		int w1=nn/p,w2=std::min((ll)nn,n/p/p);
		for(int re i=1;i<=w1;++i)
			Dec(f2[i],mul(p,dec(f2[i*p],_f)));
		for(int re i=w1+1;i<=w2;++i)
			Dec(f2[i],mul(p,dec(f1[n/i/p],_f)));
		for(int re i=nn;i>=(ll)p*p;--i)
			Dec(f1[i],mul(p,dec(f1[i/p],_f)));
	}
}

int calc(ll n,int p){
	if((ll)p*p>n)return MD(n/p);
	return MD(n/p-calc(n/p,p));
}

void Main(){
	linear_sieves();
	int T;scanf("%d",&T);
	while(T--){
		scanf("%lld",&n);
		int ans=0,las=0,now;
		for(ll l=1,r,u;(u=n/l);l=r+1)
			Inc(ans,mul(dec(now=S(r=n/u),las),C(sqrt(u)))),las=now;
		min_25_sieve();
		ll l=1,r,u;for(;(r=n/(n/l))<=nn;l=r+1);
		for(int re i=1;p[i]<=r;++i){
			int vl=calc(n,p[i]);
			Inc(ans,mul(p[i],mul(vl,MD(n-vl))));
		}for(l=r+1;(u=n/l);l=(r=n/u)+1){
			Inc(ans,mul(dec(f2[u],f2[u+1]),mul(MD(u),MD(n-u))));
		}cout<<ans<<"\n";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("APSP.in","r",stdin);
#endif
}
signed main(){file();Main();return 0;}

如果这份代码要再快一点也很简单。

把所有下标的除法,拿出来,用 double 预处理倒数,计算乘法。

一般能快个2~4倍,不过没什么意义,懒得改了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值