先讲一下原根的一些小知识。。
原根的意思是,对于一个质数p,若一个正整数a,有且只有a^(p - 1) mod p = 1,则称a为p的原根
原根的性质:(有空可以看一下http://www.infosec.sdu.edu.cn/jpkc/resource/4yuangen.pdf)
怎么找原根:?
p的原根数量=φ(φ(m))
若a为p的原根,则对于质数pi | (p-1),a^((p-1)/pi) mod p != 1
莫比乌斯反演。
设函数f,g,我们定义一种新运算f*g,表示
(f*g)(n) =
定义函数μ(n),
莫比乌斯反演公式:g= f*1 ---> f = μ*g
同时,运算*满足交换律与结合律,这也是我们用来解决数学问题的强力工具!!!
来一道比较简单的例题吧。。
比如说,求φ(m)的值。
再比如说,求(spoj gcdex)
我这里就直接贴结论好了。。f = μ * id * id
积性函数的性质:若f,g为积性函数,则h(n)=f(n)*g(n),h也为积性函数
若f = μ*g,f或g为积性函数,则另一个也是积性函数。
相关的例题,SDOI2014数表,spoj PGCD,spoj LCMSUM
推荐的论文:贾志鹏的《线性筛法与积性函数 》
相关的代码:
spoj gcdex
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAXN = 1000005;
LL F[MAXN];
int Zh[MAXN],Muti[MAXN],ti[MAXN],fai[MAXN],N,tot;
bool bz[MAXN];
void Pre_Treat()
{
N = MAXN - 5;
for(int i = 2;i <= N;i ++)
{
if (!bz[i]) Zh[++ tot] = i,Muti[i] = i,fai[i] = i - 1;
for(int j = 1;j <= tot;j ++)
if (LL(i) * Zh[j] > N) break; else
{
bz[i * Zh[j]] = 1;
if (i % Zh[j] == 0)
{
int least = i / Muti[i];
if (least == 1) fai[i * Zh[j]] = i * (Zh[j] - 1); else
fai[i * Zh[j]] = fai[least] * fai[Muti[i] * Zh[j]];
Muti[i * Zh[j]] = Muti[i] * Zh[j];
break;
}
fai[i * Zh[j]] = fai[i] * fai[Zh[j]],Muti[i * Zh[j]] = Zh[j];
}
}
for(int i = 1;i <= N;i ++) F[i] = fai[i];
for(int i = 2;i * i <= N;i ++)
{
F[i * i] += i * LL(fai[i]);
for(int j = i + 1;j * i <= N;j ++)
F[i * j] += i * LL(fai[j]) + LL(fai[i]) * j;
}
for(int i = 1;i <= N;i ++) F[i] += F[i - 1];
}
int main()
{
Pre_Treat();
while (scanf("%d", &N) != EOF && N) printf("%lld\n",F[N]);
}
spoj PGCD
#include
#include
using namespace std;
const int MAXN = 10000005;
bool bz[MAXN];
int Zh[MAXN],g[MAXN],u[MAXN],tot;
int min(int a,int b) {return a < b ? a : b;}
void Pre()
{
int N = MAXN - 5;
u[1] = 1;
for(int i = 2;i <= N;i ++)
{
if (!bz[i]) Zh[++ tot] = i,u[i] = -1,g[i] = 1;
for(int j = 1;j <= tot;j ++)
if (i * (long long)Zh[j] > N) break;else
{
bz[i * Zh[j]] = 1;
if (i % Zh[j] == 0) {g[i * Zh[j]] = u[i];break;}
u[i * Zh[j]] = u[i] * -1;
g[i * Zh[j]] = u[i] - g[i];
}
}
for(int i = 1;i <= N;i ++) g[i] += g[i - 1];
}
int main()
{
Pre();
int T,cnt = 0;scanf("%d", &T);
long long Ans = 0;
for(;T;T --)
{
int a,b;
Ans = 0;
scanf("%d%d", &a, &b);
int h = min(a,b);
for(int i = 1;i <= h;)
{
int ta = a / i,tb = b / i;
int j = min(a / ta,b / tb);
Ans += ta * (long long)tb * (g[j] - g[i - 1]);
i = j + 1;
}
printf("%lld\n", Ans);
}
}
spoj LCMSUM
#include
using namespace std;
const int MAXN = 1000005;
bool bz[MAXN];
int prime[MAXN],Ask[300005],h[MAXN],N;
long long f[MAXN];
long long sqr(int a) {return (long long)a * a;}
void Pre_Treat()
{
f[1] = 1;int tot = 0,k;
for (int i = 2;i <= N;i ++)
{
if (!bz[i]) h[i] = i,prime[++ tot] = i,f[i] = sqr(i) - i + 1;
for (int j = 1;j <= tot;j ++)
if ((k = prime[j] * i) > N) break; else
{
bz[k] = 1;
if (!(i % prime[j]))
{
if (i != h[i]) f[k] = f[i / h[i]] * f[h[i] * prime[j]];
else
f[k] = (sqr(k) * prime[j] - prime[j]) / (prime[j] + 1) + 1;
h[k] = h[i] * prime[j];
break;
}
else
f[k] = f[prime[j]] * f[i],h[k] = prime[j];
}
}
}
void read(int &a)
{
a = 0;char c;
while (c = getchar(),c < '0' || c > '9');
a = c - 48;
while (c = getchar(),c >= '0' && c <= '9') a = a * 10 + c - 48;
}
int main()
{
int T;read(T);
for (int i = 1;i <= T;i ++) {read(Ask[i]);if (Ask[i] > N) N = Ask[i];}
Pre_Treat();
for (int i = 1;i <= T;i ++) printf("%lld\n",(f[Ask[i]] + 1) * Ask[i] / 2);
}