BZOJ 3529 SDOI2014 数表 莫比乌斯反演+树状数组

题目大意:令F(i)为i的约数和,多次询问对于1<=x<=n,1<=y<=m,F(gcd(x,y))<=a的所有数对(x,y),求ΣF(gcd(x,y))%(2^31)

n,m<=10^5,a<=10^9

首先如果不考虑a的限制 令g(i)为1<=x<=n,1<=y<=m,gcd(x,y)=i的数的个数

那么显然有


利用线性筛处理出F(i) 那么答案显然是


治好了我多年的公式恐惧症。。。

现在我们只需要求出的前缀和 这个问题就能在O(√n)的时间内出解

枚举每一个i 枚举i的倍数 暴力即可求出这个函数 然后处理前缀和即可 复杂度是O(nlogn)的

那么现在有了a的限制怎么搞呢?

我们发现对答案有贡献的i只有F(i)<=a的i 那么我们将询问按照a从小到大排序 将F(i)从小到大排序 每次询问将<=a的F(i)按照之前的方式暴力插入 用树状数组来维护这个前缀和

这题就搞出来了

时间复杂度O(nlog^2n+q√nlogn) 有点卡。。。取模那里自然溢出int就行了 最后再对2147483647取一下&即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct query{
	int n,m,a,_id;
	bool operator < (const query &x) const
	{
		return a < x.a ;
	}
}queries[20200];
int T,ans[20200];
pair<int,int>f[M];
int mu[M],prime[M],tot;
bool not_prime[M];
void Linear_Shaker()
{
	static int min_factor_a[M],min_factor_sum[M];
	/*
	12=2*2*3 F(12)=(1+2+4)*(1+3)
	min_factor_a[12]=2*2=4
	min_factor_sum[12]=1+2+4=7
	*/
	int i,j;
	f[1]=make_pair(1,1);mu[1]=1;
	for(i=2;i<=100000;i++)
	{
		f[i].second=i;
		if(!not_prime[i])
		{
			prime[++tot]=i;
			min_factor_a[i]=i;
			f[i].first=min_factor_sum[i]=i+1;
			mu[i]=-1;
		}
		for(j=1;j<=tot&&prime[j]*i<=100000;j++)
		{
			not_prime[prime[j]*i]=1;
			if(i%prime[j]==0)
			{
				f[prime[j]*i].first=f[i].first/min_factor_sum[i]*
					(min_factor_sum[prime[j]*i]=min_factor_sum[i]+min_factor_a[i]*prime[j]);
				min_factor_a[prime[j]*i]=min_factor_a[i]*prime[j];
				mu[prime[j]*i]=0;
				break;
			}
			f[prime[j]*i].first=f[i].first*(prime[j]+1);
			min_factor_a[prime[j]*i]=prime[j];
			min_factor_sum[prime[j]*i]=prime[j]+1;
			mu[prime[j]*i]=-mu[i];
		}
	}
}
namespace BIT{
	int c[M];
	inline void Update(int x,int y)
	{
		for(;x<=100000;x+=x&-x)
			c[x]+=y;
	}
	inline int Get_Ans(int x)
	{
		int re=0;
		for(;x;x-=x&-x)
			re+=c[x];
		return re;
	}
}
int Query(int n,int m)
{
	int i,last,re=0;
	if(n>m) swap(n,m);
	for(i=1;i<=n;i=last+1)
	{
		last=min(n/(n/i),m/(m/i));
		re+=(n/i)*(m/i)*(BIT::Get_Ans(last)-BIT::Get_Ans(i-1));
	}
	return re&2147483647;
}
int main()
{
	int i,j,k;
	Linear_Shaker();
	sort(f+1,f+100000+1);
	cin>>T;
	for(i=1;i<=T;i++)
		scanf("%d%d%d",&queries[i].n,&queries[i].m,&queries[i].a),queries[i]._id=i;
	sort(queries+1,queries+T+1);
	for(i=1,j=1;i<=T;i++)
	{
		for(;j<=100000&&f[j].first<=queries[i].a;j++)
			for(k=f[j].second;k<=100000;k+=f[j].second)
				BIT::Update(k,f[j].first*mu[k/f[j].second]);
		ans[queries[i]._id]=Query(queries[i].n,queries[i].m);
	}
	for(i=1;i<=T;i++)
		printf("%d\n",ans[i]);
	return 0;
}


评论 2 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

PoPoQQQ

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值