P3312 数表

P3312 数表

题意

求出

i=1nj=1mσ(gcd(i,j))[σ(gcd(i,j))a]

其中 σ 表示约数和。

思路/推导

考虑没有 a 的限制的情况。

ans=d=1min(n,m)σ(d)i=1ndj=1md[gcd(i,j)=1]=d=1min(n,m)σ(d)i=1ndj=1mdpipjμ(p)=d=1min(n,m)σ(d)p=1min(n,m)dμ(p)ndpmdp=T=1min(n,m)d=1Tσ(d)μ(Td)nTmT

考虑加入 a 的限制。将询问按照 a 大小离线,然后用一个树状数组维护 dσ(d)μ(Td) 的前缀和即可。

具体是将线性筛出的所有数的约数和从小到大进行排序,在从小到大查询的时候进行更新。

不会筛 σ 的可以看我的另一篇博客

时间复杂度瓶颈在于查询,需要用到数论分块,为 O(qnlogn)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<utility>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=x*10+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=1e5+10,maxm=2e4+10,N=1e5;
	int n,p[maxn/10],mu[maxn],tot,c[maxn],ans[maxm],g[maxn];
	pair<int,int> f[maxn];
	bool mark[maxn];
	inline void insert(int x,int k){for(;x<=N;x+=x&-x) c[x]+=k;}
	inline int query(int x){int ans=0;for(;x;x-=x&-x) ans+=c[x];return ans;}
	struct Query{
		int n,m,a,id;
		inline bool operator < (const Query& zp) const {return a<zp.a;}
		inline int solve(){
			if(n>m) swap(n,m);
			int ans=0;
			for(int l=1,r;l<=n;l=r+1)
				r=min(n/(n/l),m/(m/l)),ans+=((query(r)-query(l-1))*(n/l)*(m/l));
			return ans;
		}
	}q[maxm];
	inline void work(){
		mu[1]=1;
		f[1]=make_pair(1,1);
		for(int i=2;i<=N;i++){
			if(!mark[i]) p[++tot]=i,mu[i]=-1,g[i]=i+1,f[i]=make_pair(i+1,i);
			for(int j=1,tmp;j<=tot and (tmp=i*p[j])<=N;j++){
				mark[tmp]=true;
				if(i%p[j]==0){
					mu[tmp]=0;
					g[tmp]=g[i]*p[j]+1;
					f[tmp]=make_pair(f[i].first/g[i]*g[tmp],tmp);
					break;
				}
				mu[tmp]=-mu[i];
				g[tmp]=p[j]+1;
				f[tmp]=make_pair(f[i].first*f[p[j]].first,tmp);
			}
		}
		sort(f+1,f+1+N);
		n=read();
		for(int i=1;i<=n;i++) q[i].n=read(),q[i].m=read(),q[i].a=read(),q[i].id=i;
		sort(q+1,q+1+n);
		for(int i=1,j=1;i<=n;i++){
			while(f[j].first<=q[i].a and j<=N){
				for(int k=f[j].second;k<=N;k+=f[j].second) insert(k,f[j].first*mu[k/f[j].second]);
				j++;
			}
			ans[q[i].id]=q[i].solve();
		}
		for(int i=1;i<=n;i++) printf("%d\n",ans[i]&(~(1<<31)));
	}
}
signed main(){
	star::work();
	return 0;
}
posted @   Star_Cried  阅读(73)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示