bzoj2226(数论)

本来找了O(n^1/2)的方法,对拍过后没问题,交上竟然超时,果然复杂度需要更低,然而这就属于不在我的能力范围之内的积性函数的运用了

但是,发现只要这个知识点我学过,那么只要时间足够,就一定能想出做法,在做这个题时想过放弃,但是坚持就会有好的结果

根n算法

通过打表,找了一下规律,同时发现gcd(a,n)==gcd(b,n)==d,那么,他们最大公约数相同,化成lcm就是a*n/d,b*nd,发现是可以合并的式子

也就是说只要找到所有的因子,并在O(1)的时间求出所要gcd==d数的总和就可以算出来

36的表

36
36 1 1 36//可以发现第一个数+最后一个数==n,且中间也是如此,所以只需求出gcd==d的个数然后代公式即可
36 5 1 180//当然是通过phi【n/i】来求个数喽~
36 7 1 252
36 11 1 396
36 13 1 468
36 17 1 612
36 19 1 684
36 23 1 828
36 25 1 900
36 29 1 1044
36 31 1 1116
36 35 1 1260

36 2 2 36
36 10 2 180
36 14 2 252
36 22 2 396
36 26 2 468
36 34 2 612
36 3 3 36
36 15 3 180
36 21 3 252
36 33 3 396
36 4 4 36
36 8 4 72
36 16 4 144
36 20 4 180
36 28 4 252
36 32 4 288
36 6 6 36
36 30 6 180
36 9 9 36
36 27 9 108
36 12 12 36
36 24 12 72
36 18 18 36
36 36 36 36

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int pri[1000009],phi[1000009];
bool b[1000009];
int main()
{
	int T;
	phi[1]=1;
	int N=1000000;
	for (int i=2;i<=N;i++)
	{
		if (!b[i])
		{
			phi[i]=i-1;
			pri[++pri[0]]=i;
		}
		for (int j=1;j<=pri[0]&&i*pri[j]<=N;j++)
		{
			b[pri[j]*i]=true;
			if (i%pri[j]==0)
			{
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}else phi[i*pri[j]]=phi[i]*(pri[j]-1);
		}
	}///get phi
	//for (int i=1;i<=100;i++) printf("%d ",phi[i]);
	scanf("%d",&T);
	while (T--)
	{
		long long n;
		scanf("%lld",&n);
		long long ans=0;
		int sq=(long long)sqrt(n);
		for (int i=1;i<=sq;i++) if (n%i==0)
		{
			int k=i;
			ans+=n/i*n*phi[n/i]/2;
			k=n/i;
			if (k==i) continue;//不要重复计算,把因数遍历,6*6=36,只有一种
			if (k!=n) ans+=n/k*n*phi[n/k]/2;else ans+=n;//注意特殊情况的处理
		}
		if (n==1) ans=1;
		printf("%lld\n",ans);
	}
	return 0;
} 
打表代码

#include<cstdio>
#include<algorithm>

using namespace std;

int n;
struct aa
{
	int gcd,a,b;
	bool operator <(const aa bb)const
	{
		if (gcd!=bb.gcd)return gcd<bb.gcd;
		else return b<bb.b;
	}	
}x[1009];
int gcd(int a,int b)
{
	return b? gcd(b,a%b):a;
}
int main()
{
	while (scanf("%d",&n)!=EOF)
	{
		int k=0;
		for (int i=1;i<=n;i++) x[i].gcd=gcd(n,i),x[i].b=i;
		sort(x+1,x+n+1);
		for (int i=1;i<=n;i++) printf("%d %d %d %d\n",n,x[i].b,x[i].gcd,n*x[i].b/x[i].gcd);
	}
	return 0;
}

学一下标准做法

Problem Three:[SPOJ LCMSUM]sigma(lcm(i, n)),1 ≤ i ≤ n. 多组询问

令S(i)= Sigma(i,i|n) =>iphi(i)/2

则Ans=nSigma(S(d),d|n)

直接做预处理phi(i),然后枚举约数,复杂度:O(N)-O(sqrt(N)).

令G(i)=iphi(i)/2,i|n,不难发现Ans=N((G(N)-1)/2+1).

由于可证G(N)是积性函数,所以我们可以在O(N)的时间内算出G(N),询问O(1).



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值