[SDOI2014]数表【莫比乌斯反演】

不得已来写一下这道题的博客,,这道题很有思想代表性。

传送门ovo

我们设g(i)=\sum_{d|i}d也就是约数和。且令n<=m。

则目标是Ans=\sum_{i=1}^n\sum_{j=1}^m\sum_{gcd(i,j)<=a}g(gcd(i,j))

我们设F(x)=\sum_{i=1}^{n}\sum_{j=1}^m[gcd(i,j)==x]=\sum_{x|d}\mu(\frac{d}{x})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d}\right \rfloor即gcd为x的数对个数和。

如果我们忽略a的限制,则目标是

Ans=\sum_{i=1}^nF(i)g(i)=\sum_{i=1}^ng(i)\sum_{i|d}\mu({\left \lfloor \frac{d}{i} \right \rfloor})\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor=\sum_{d=1}^n\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor\sum_{i|d}\mu({\left \lfloor \frac{d}{i} \right \rfloor})g(i)

我们令f(d)=\sum_{i|d}\mu({\left \lfloor \frac{d}{i} \right \rfloor})g(i),因为这是一个狄利克雷卷积形式,可以nlogn筛出来。

则原来的Ans=\sum_{d=1}^n\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor)f(d)

则f(d)可以求前缀和,然后整除分块之类地。

但最重要的!有a的限制。

我们的处理方式是将询问离线按照a排序,对于所有g(i)排序,原来的做法是选择数字枚举约数,我们枚举约数用树状数组计算其对后面所有数的贡献。

一个一个加就行,代码好好看

#include<bits/stdc++.h>
#define in read()
using namespace std;
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();
		if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}
	return cnt*f;
} 
int mu[100003],F[100003];
int prime[100003],cnt,vis[100003];
void prepare(){
	mu[1]=1;vis[1]=1;
	for(int i=2;i<=100003;i++){
		if(!vis[i])prime[++cnt]=i,mu[i]=-1;
		for(int j=1;prime[j]*i<=100003&&j<=cnt;j++){
			vis[prime[j]*i]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;
				break;
			}
			mu[prime[j]*i]=-mu[i];
		}
	}
	for(int i=1;i<=100003;i++){
		for(int j=i;j<=100003;j+=i){
			F[j]+=i;
		}
	}
}
struct quee{
	int n,m,a,id;
}que[100003];
struct node{
	int id,g;
}A[100003];
struct tree{
	int sum;
}t[100003];
bool cmp1(quee a,quee b){
	return a.a<b.a;
} 
bool cmp2(node a,node b){
	return a.g<b.g;
}
int lowbit(int u){
	return u&(-u);
}
void add(int u,int key){
	while(u<=100003){
		t[u].sum+=key;
		u+=lowbit(u);
	}
}
int query(int u){
	int ans=0;
	while(u){
		ans+=t[u].sum;
		u-=lowbit(u);
	}
	return ans;
}
int calc(int nn,int mm){
	int ret=0;if(nn>mm)swap(nn,mm);
	for(int l=1,r;l<=min(nn,mm);l=r+1){
		r=min(nn/(nn/l),mm/(mm/l));
		ret+=(nn/l)*(mm/l)*(query(r)-query(l-1)); 
	}
	return ret;
}
int main(){
	prepare();
//	for(int i=1;i<=10;i++){
//		cout<<mu[i]<<" ";
//	}cout<<endl;
	unsigned int Ans[100003];
	int q=in;
	for(int i=1;i<=q;i++){
		que[i].n=in;que[i].m=in;que[i].a=in;que[i].id=i;
	} 
	sort(que+1,que+q+1,cmp1);
	for(int i=1;i<=100003;i++){
		A[i].g=F[i];A[i].id=i;
	}
	sort(A+1,A+100003+1,cmp2);
	int tot=0;
	for(int i=1;i<=q;i++){
		while(tot<=100003&&A[tot+1].g<=que[i].a){
			++tot;
			for(int j=1;j*A[tot].id<=100003;j++){
				add(j*A[tot].id,mu[j]*A[tot].g);
			}
		}
		Ans[que[i].id]=calc(que[i].n,que[i].m);
	}
	for(int i=1;i<=q;i++){
		printf("%d\n",Ans[i]&2147483647);
	}
	return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值