[SDOI2014]数表

数表

题解

挺简单的一道题。

题目可以简化为 ∑ i ∈ [ 1 , n ] ∑ j ∈ [ 1 , m ] g [ ( i , j ) ] [ g [ ( i , j ) ] ≤ a ] \sum_{i\in [1,n]}\sum_{j\in[1,m]}g[(i,j)][g[(i,j)]\leq a] i[1,n]j[1,m]g[(i,j)][g[(i,j)]a]。其中, g ( x ) g(x) g(x)表示 x x x的约数和。虽然原题也够简洁了

我们还是先推一下式子。
f ( x ) = ∑ i ∈ [ 1 , n ] ∑ j ∈ [ 1 , m ] [ ( i , j ) = = x ] f(x)=\sum_{i\in[1,n]}\sum_{j\in[1,m]}[(i,j)==x] f(x)=i[1,n]j[1,m][(i,j)==x]
显然,原式可化为 ∑ i ∈ [ 1 , min ⁡ ( n , m ) ] g ( i ) f ( i ) [ g ( i ) ≤ a ] \sum_{i\in [1,\min(n,m)]}g(i)f(i)[g(i)\leq a] i[1,min(n,m)]g(i)f(i)[g(i)a]
但很明显,这个 f ( x ) f(x) f(x)是很难直接求出来的,考虑莫比乌斯反演。
F ( x ) = ∑ i ∈ [ 1 , n ] ∑ j ∈ [ 1 , m ] [ x ∣ ( i , j ) ] F(x)=\sum_{i\in[1,n]}\sum_{j\in[1,m]}[x|(i,j)] F(x)=i[1,n]j[1,m][x(i,j)]
此时有, F ( x ) = ∑ d ∣ x f ( d ) = ⌊ n x ⌋ ⌊ m x ⌋ F(x)=\sum_{d|x}f(d)=\left\lfloor \frac{n}{x} \right\rfloor\left\lfloor \frac{m}{x} \right\rfloor F(x)=dxf(d)=xnxm
于是, f ( x ) = ∑ x ∣ d μ ( d x ) F ( d ) = ∑ x ∣ d μ ( d x ) ⌊ n d ⌋ ⌊ m d ⌋ f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)=\sum_{x|d}\mu(\frac{d}{x})\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor f(x)=xdμ(xd)F(d)=xdμ(xd)dndm
代入原式,
= ∑ i ∈ [ 1 , min ⁡ ( n , m ) ] g ( i ) ∑ i ∣ d μ ( d i ) ⌊ n d ⌋ ⌊ m d ⌋ [ g ( i ) ≤ a ] =\sum_{i\in [1,\min(n,m)]}g(i)\sum_{i|d}\mu(\frac{d}{i})\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor[g(i)\leq a] =i[1,min(n,m)]g(i)idμ(id)dndm[g(i)a]
= ∑ d ∈ [ 1 , min ⁡ ( n , m ) ] ⌊ n d ⌋ ⌊ m d ⌋ ∑ i ∣ d [ g ( i ) ≤ a ] g ( i ) μ ( d i ) =\sum_{d\in[1,\min(n,m)]}\left\lfloor \frac{n}{d} \right\rfloor\left\lfloor \frac{m}{d} \right\rfloor\sum_{i|d}[g(i)\leq a]g(i)\mu (\frac{d}{i}) =d[1,min(n,m)]dndmid[g(i)a]g(i)μ(id)

很明显,只有当后面的 [ g ( i ) ≤ a ] [g(i)\leq a] [g(i)a]成立时,后面式子的值才会不为0。
于是,我们考虑将询问离线下来,通过 a a a值的大小排序,将每个 g ( i ) g(i) g(i)依次加入。
我们可以通过树状数组来维护后面的一段的 g ( i ) g(i) g(i),每次当 a a a值扩大时再将 g ( i ) g(i) g(i)插入进去。
至于 g ( i ) g(i) g(i)的值,我们可以在最开始预处理莫比乌斯函数时一块求出。
当然, O ( ( q + n ) n ) O\left((q+n)n\right) O((q+n)n)的时间复杂度时明显过不了的,考虑数论分块。
因为数论分块涉及到区间和的动态维护,我们需要通过树状数组之类的数据结构来维护每个项的值。
当插入 g ( i ) g(i) g(i)的时候会影响到 n i \frac{n}{i} in个项的值,总共会有 n l n   n nln\, n nlnn次插入操作。
总时间复杂度 O ( n log ⁡   n ln ⁡   n + q n l o g   n ) O\left(n\log\, n\ln\,n+q\sqrt{n}log\, n\right) O(nlognlnn+qn logn),可以过题。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAXN 100010
#define lowbit(x) (x&-x)
typedef long long LL;
const int INF=0x7f7f7f7f;
const LL mo=(1<<31);
typedef pair<int,int> pii;
#define gc() getchar()
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int mobius[MAXN],prime[MAXN],cntp,q,tot;
LL ans[MAXN],tr[MAXN],g[MAXN];
bool oula[MAXN];
struct ming{int n,m,a,id;}s[MAXN];
struct number{LL d;int id;}f[MAXN];
bool cmp(ming x,ming y){return x.a<y.a;}
bool cmp1(number x,number y){return x.d<y.d;}
void init(){
	mobius[1]=1;f[1].d=f[1].id=1;
	for(int i=2;i<=1e5;i++){
		f[i].id=i;if(!oula[i])prime[++cntp]=i,mobius[i]=-1,g[i]=i+1,f[i].d=i+1;
		for(int j=1;j<=cntp&&i*prime[j]<=1e5;j++){
			oula[i*prime[j]]=1;
			if(i%prime[j]==0){
				g[i*prime[j]]=1ll*g[i]*prime[j]+1LL;
				f[i*prime[j]].d=1ll*f[i].d/g[i]*g[i*prime[j]];
				mobius[i*prime[j]]=0;break;
			}
			mobius[i*prime[j]]=-mobius[i];
			f[i*prime[j]].d=1ll*f[i].d*f[prime[j]].d;
			g[i*prime[j]]=1ll*prime[j]+1LL;
		}	
	}
}
void insert(int x,LL w){while(x<=tot)tr[x]+=w,x+=lowbit(x);}
LL query(int x){LL res=0;while(x)res+=tr[x],x-=lowbit(x);return res;}
signed main(){
	read(q);init();
	for(int i=1;i<=q;i++)
		read(s[i].n),read(s[i].m),read(s[i].a),
		s[i].id=i,tot=max(max(s[i].m,s[i].n),tot);
	sort(s+1,s+q+1,cmp);sort(f+1,f+tot+1,cmp1);int j=1;
	for(int i=1;i<=q;i++){
		while(j<=tot&&f[j].d<=s[i].a){
			for(int k=f[j].id;k<=tot;k+=f[j].id)
				insert(k,f[j].d*mobius[k/f[j].id]);
			j++;
		}
		for(int l=1,r;l<=min(s[i].n,s[i].m);l=r+1)
			r=min(s[i].n/(s[i].n/l),s[i].m/(s[i].m/l)),
			ans[s[i].id]+=1ll*(query(r)-query(l-1))*(s[i].n/l)*(s[i].m/l);
	}
	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]&(~mo));
	return 0;
}

谢 谢 \color{red}{谢谢}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值