luogu1891 疯狂lcm ??欧拉反演?

link

给定正整数N,求LCM(1,N)+LCM(2,N)+...+LCM(N,N)。

多组询问,1≤T≤300000,1≤N≤1000000

\(\sum_{i=1}^nlcm(i,n)\)

\(=\sum_{i=1}^n\frac{in}{\gcd(i,n)}\)

\(=n\sum_{p|n}\frac 1 p\sum_{i=1}^ni[\gcd(i,n)=p]\)

\(=n\sum_{p|n}\sum_{i=1}^{n/p}i[\gcd(i,\frac n p)=1]\)//注意这里是n/p而不是n,mdzz第一次我这里推错了

\(=n\sum_{p|n}\sum_{i=1}^{p}i[\gcd(i,p)=1]\)

然后就可以套用公式\(\sum_{i=1}^ni[\gcd(i,n)=1]=\frac {[n=1]+n\varphi(n)}{2}\)

\(=n\sum_{p|n}\frac{[p=1]+p\varphi(p)}{2}\)

线性筛,对于所有\(p\)预处理所有的\([p=1]+\varphi(p)*p\)

然后枚举倍数,在100Wlog100W时间复杂度内求出所有N的答案

代码没时间写了,明天放

然而还是在3分钟内写出了代码

#include <cstdio>
using namespace std;

const int fuck = 1000000;
bool vis[fuck + 10];
int prime[fuck + 10], tot;
int phi[fuck + 10];
long long ans[fuck + 10];

int main()
{
	phi[1] = 1;
	for (int i = 2; i <= fuck; i++)
	{
		if (vis[i] == false) prime[++tot] = i, phi[i] = i - 1;
		for (int j = 1; j <= tot && i * prime[j] <= fuck; j++)
		{
			vis[i * prime[j]] = true;
			if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; }
			else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
	for (int p = 1; p <= fuck; p++)
	{
		long long sb = (p == 1) + phi[p] * (long long)p;
		for (int b = p; b <= fuck; b += p)
		{
			ans[b] += sb;
		}
	}
	int t; scanf("%d", &t);
	while (t --> 0)
	{
		int x;
		scanf("%d", &x);
		printf("%lld\n", ans[x] * x / 2);
	}
	return 0;
}
posted @ 2019-01-20 21:56  ghj1222  阅读(225)  评论(0编辑  收藏  举报