P3312 [SDOI2014]数表

[SDOI2014]数表

题目描述

有一张 n×m 的数表,其第 i 行第 j 列(1in1jm)的数值为能同时整除 ij 的所有自然数之和。给定 a,计算数表中不大于 a 的数之和。

1n,m1051Q2×104

思路点拨

我们先考虑对于两个数 i,j ,那些数才会同时整除 i,j 。显然这些数都是 i,j 的公约数。我们定义 f(x) 表示 x 的约数和,即 d|xd 。对于 i,j 而言,满足条件的数是 f(gcd(i,j)) 。这一点很好理解。我们先不看 a 的约束,那么答案就是:

i=1nj=1mf(gcd(i,j))

d=1nf(d)i=1nj=1m[gcd(i,j)=d]

d=1nf(d)i=1ndj=1md[gcd(i,j)=1]

d=1nf(d)k=1ndμ(k)ndkmdk

我们;令 T=kd ,那么

T=1nnTmT(d|Tf(d)μ(Td))

这个式子需要富比尼定理优化。可以 O(n) 计算,前提是后边括号内的式子在 O(nlogn) 的时间预处理。

但是这道题目还没有写完,因为有 a 的至于限制,所以原式准确表达为:

T=1nnTmT(d|Tf(d)μ(Td)[f(d)a])

我们可以将全部的询问离线,按照 a 为关键字排序,每一次就将 f(x)af(x)0 设为它本身的值。我们富比尼定理计算上面的式子的时候,g(x)=d|Tf(d)μ(Td) ,需要以区间的形式计算,所以我们还需要使用一个树状数组来进行单点修改,区间加和。

时间复杂度 O(nlog2n+qnlogn)

放出一份代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-f;
		ch=getchar(); 
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int mod=(1ll<<31);
int T;
const int MAXN=1e5+10,N=1e5;
struct node{
	int x,id;
	bool friend operator<(const node &A,const node &B){
		return A.x<B.x;
	}
}f[MAXN];
struct getans{
	int x,y,w;
	int id;
	bool friend operator<(const getans &A,const getans &B){
		return A.w<B.w;
	} 
}q[MAXN];
int ans[MAXN],mu[MAXN];
bool vis[MAXN];
void init(){
	for(int i=1;i<=N;i++){
		f[i].id=i;
		mu[i]=1;
		for(int j=i;j<=N;j+=i)
			f[j].x+=i;
	}
	sort(f+1,f+N+1);
	for(int i=2;i<=N;i++){
		if(vis[i]) continue;
		mu[i]=-1;
		for(int j=i*2;j<=N;j+=i){
			vis[j]=1;
			mu[j]=-mu[j];
			if(j%(i*i)==0) mu[j]=0;
		}
	}
}
int g[MAXN];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int w){
	for(int i=x;i<=N;i+=lowbit(i))
		g[i]=(g[i]+w)%mod;
}
int query(int x){
	int cnt=0;
	for(int i=x;i;i-=lowbit(i))
		cnt=(cnt+g[i])%mod;
	return cnt;
}
signed main(){
	init();
	int T=read();
	for(int i=1;i<=T;i++){
		q[i].x=read(),q[i].y=read(),q[i].w=read();
		q[i].id=i;
	}
	sort(q+1,q+T+1);
	int last=1;
	for(int i=1;i<=T;i++){
		while(last<=N&&f[last].x<=q[i].w){
			int d=f[last].id;
			for(int T=d;T<=N;T+=d)
				add(T,(f[last].x*mu[T/d]+mod*100)%mod);
			last++;
		}
		int l=1,r=0,n=q[i].x,m=q[i].y;
		while(l<=min(n,m)){
			r=min(n/(n/l),m/(m/l));
			ans[q[i].id]=(ans[q[i].id]+(query(r)-query(l-1)+mod)*(n/l)%mod*(m/l))%mod;
			l=r+1;
		}
	}
	for(int i=1;i<=T;i++) cout<<ans[i]<<endl;
	return 0; 
}
posted @   Diavolo-Kuang  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示