题目链接:bzoj1408
—————————————-
概述
题目的意思比较复杂,大致如下:
给定一个数n,将它所有约数分为三类:
- 能分解成偶数个不同奇质数的乘积.(质因数的指数小于等于1)
- 能分解成奇数个不同奇质数的乘积.(质因数的指数小于等于1)
- 不满足1、2的约数.
注:1不算任何数的约数.
例如:对于正整数90:
- 一类约数:15.
- 二类约数:3、5.
- 三类约数:2、6、9、10、18、30、45、90.
现在告诉你正整数n,让你求得其一类约数的欧拉函数之和,二类约数的欧拉函数之和,三类约数的欧拉函数之和。结果均对10000取模。
我们将 n 质因数分解:
题目将给出
k
,以及
—————————————-
题解
我们发现,上面三类约数的并集很显然就是 n 的所有约数(不包括1)
由于正整数
(不清楚的可以参考我之前的博客:常见函数讲解1:欧拉函数,其中“扩展性质”中给出了上式的证明)
所以,我们设三类约数的欧拉函数之和分别为 Ans1、Ans2、Ans3 ,那么显然有:
也就是说,假如我们求得了 Ans1、Ans2 ,那 Ans3 自然能得知了。
那么我们如何求得 Ans1 和 Ans2 呢?
我们发现,对于一个一类约数
a=∏mi=1pi
,其中
m
为偶数,我们对
我们顺着这个思路想,我们用前 i−1 个质因数构成的一类约数,只要乘上 pi 那么就能把他们全都变成二类约数了。
设
s1i
为前
i
个质因数构成的一类约数的欧拉函数之和,
很明显, Ans1=s1k, Ans2=s2k 。所以我们只要初始化 s10=s20=0 ,然后 O(n) 的递推即可得到 Ans1 和 Ans2 的值。
至于 Ans3 嘛,之前得出了 Ans3=n−1−Ans1−Ans2 这样的结论,直接计算即可。别忘了取模。
—————————————-
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#define ll long long
#define For(i,j,k) for(register int i=j; i<=(int)k; ++i)
#define Forr(i,j,k) for(register int i=j; i>=(int)k; --i)
#define INF 0x3f3f3f3f
using namespace std;
const int mo = 10000;
const int maxn = 1000+5;
int n, x, Ans1, Ans2, Ans3;
int p[maxn], e[maxn];
inline int ksm(int x, int y){
int back = 1;
x %= mo;
while(y){
if(y & 1) back = back*x%mo;
x = x*x%mo;
y >>= 1;
}
return back;
}
int main(){
scanf("%d", &n);
For(i, 1, n) scanf("%d%d", &p[i], &e[i]);
int s1, s2;
For(i, 1, n){
if(p[i] == 2) continue;
s1 = (Ans1 + Ans2*(p[i]-1)%mo)%mo;
s2 = ((Ans2+p[i]-1)%mo + Ans1*(p[i]-1)%mo)%mo;
Ans1 = s1, Ans2 = s2;//递推求得Ans1和Ans2.
}
x = 1;
For(i, 1, n)
x = x*ksm(p[i], e[i])%mo;//计算n在模10000意义下的值.
Ans3 = (x-Ans1-Ans2-1)%mo;//计算Ans3.
while(Ans3 < 0) Ans3 += mo;//防止Ans3爆负.
printf("%d\n%d\n%d", Ans1, Ans2, Ans3);
return 0;
}
—————————————-
小结
本题的关键在于找到那个递推式计算 Ans1 和 Ans2 ,结合欧拉函数的性质就能快速计算Ans3。作者想了1h小时无果,膜拜大佬之后便有了灵感。果然,分析性质这儿技能还需要多锻炼才能提高。
—————————————-
——wrote by miraclejzd.