51nod1222 最小公倍数计数

题目描述

定义F(n)表示最小公倍数为n的二元组的数量。
即:如果存在两个数(二元组)X,Y(X <= Y),它们的最小公倍数为N,则F(n)的计数加1。
例如:F(6) = 5,因为[2,3] [1,6] [2,6] [3,6] [6,6]的最小公倍数等于6。

给出一个区间[a,b],求最小公倍数在这个区间的不同二元组的数量。
例如:a = 4,b = 6。符合条件的二元组包括:
[1,4] [2,4] [4,4] [1,5] [5,5] [2,3] [1,6] [2,6] [3,6] [6,6],共10组不同的组合。
输入
输入数据包括2个数:a, b,中间用空格分隔(1 <= a <= b <= 10^11)。
输出
输出最小公倍数在这个区间的不同二元组的数量。
输入样例
4 6
输出样例
10

题解

由于是自己想的所以厚颜无耻打上原创

min25裸题(并不)
发现有lcm和1011和函数前缀和,那么肯定是莫比乌斯反演 or min25
又发现反演不行,所以就是min25了
是不是很显然


发现答案的函数并不好求,所以设F(n)表示最小公倍数为n的不同二元组的数量(不考虑X和Y的大小关系)
可以发现若 n = ∏ a i p i n=\prod {ai^{pi}} n=aipi,则X和Y中一定有一个pi的指数为ai,每个pi就有2ai+1种分配方法(一个指数为0~ai,另一个为ai,两个都为ai的算重了一遍要减去)
那么 F ( n ) = ∏ 2 ∗ a i + 1 F(n)=\prod {2*ai+1} F(n)=2ai+1

min25三 要 素:
F ( x ) F(x) F(x)是积性函数
显然
F ( p ) F(p) F(p)能表示成多项式的形式(p是质数)
F ( p ) = F ( p 1 ) = 3 F(p)=F(p^1)=3 F(p)=F(p1)=3
F ( p c ) F(p^c) F(pc)能快速计算
F ( p c ) = 2 c + 1 F(p^c)=2c+1 F(pc)=2c+1

最后为n的答案是(∑F(i) +n+1)/2(x=y=n只算了一次)
F(1)=1
没了

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

long long b[316228];
int p[316228];
long long P[316228];
bool bz[316228];
long long g[316228];
long long G[316228];
int i,j,k,l,len;
long long A,B,N,s,S,I,I2;

void init(long long n,long long N)
{
	fo(i,1,N) g[i]=(i-1)*3,G[i]=(b[i]-1)*3;
	
	memset(bz,0,sizeof(bz));
	len=0;
	
	fo(I,2,N)
	{
		if (!bz[I])
		{
			p[++len]=I;
			P[len]=(long long)I*I;
			I2=I*I;
			
			fo(i,1,N)
			if (I2<=b[i])
			{
				s=b[i]/I;
				
				if (s>N)
				G[i]-=G[n/s]-g[I-1];
				else
				G[i]-=g[s]-g[I-1];
			}
			else
			break;
			fd(i,N,1)
			if (I2<=i)
			{
				s=i/I;
				g[i]-=g[s]-g[I-1];
			}
			else
			break;
		}
		
		fo(j,1,len)
		if ((long long)I*p[j]<=N)
		{
			bz[I*p[j]]=1;
			if (!(I%p[j])) break;
		}
		else
		break;
	}
}

long long F(long long p,int c)
{
	return c*2+1;
}

long long Work(long long n,long long _n,int j)
{
	long long ans,_n2;
	int i,e;
	
	if (_n>N)
	ans=G[n/_n];
	else
	ans=g[_n];
	ans-=(j-1)*3;
	
	fo(i,j,len)
	if (P[i]<=_n)
	{
		_n2=_n/p[i];
		e=1;
		
		while (_n2>=p[i])
		{
			ans+=(Work(n,_n2,i+1)+(e>1))*F(p[i],e);
			++e;
			_n2/=p[i];
		}
		ans+=(e>1)*F(p[i],e);
	}
	else
	break;
	
	return ans;
}

long long work(long long n)
{
	if (!n) return 0;
	
	N=floor(sqrt(n));
	fo(i,1,N) b[i]=n/i;
	init(n,N);
	
	return (Work(n,n,1)+1+n)/2;
}

int main()
{
//	freopen("51nod1222.in","r",stdin);
	
	scanf("%lld%lld",&A,&B);
	printf("%lld\n",work(B)-work(A-1));
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值