数论部分补题的一点小收获
连续5天的数学部分的知识学习已经结束了,今天本该好好消化吸收前4天所讲的知识点,最终却睡了一下午玩了一晚上。。。翻了翻早上半天时间补题的记录,还是总结一点东西吧,大部分还是昨天的研究成果。
首先是K题:POJ 1284 Primitive Roots 题意很好理解,就是给定一个奇素数p,求它的原根个数。
看懂了原根的定义以后,开始以为要一个一个检验计数,觉得太麻烦了,当然这个坑待填(求奇素数的最小原根)。这里讲清楚了原根个数的计算公式:一个整数m的原根个数为phi(phi(m))。特别地,p为素数时,phi(p)=p-1,那么p的原根个数就是phi(p-1)。那么这题就完结了。
接下来说I题:POJ 3641 Pseudoprime numbers 伪素数——以我理解就是满足费马小定理的非素数。
在维基百科欧拉函数的词条刚好看到了费马小定理的表述,这里先讲是因为昨天做这题刚好用错过。
贴上部分代码说明:
bool isPrime(ll x) { // 判断是否时素数,配合素数打表使用 if(x<=40000) return !notprime[x]; for(int i=0;i<cnt && primes[i]<=x/primes[i];i++) { if(x%primes[i]==0) return false; } return true; } ll pow_mod(ll a, ll n, ll mod) { // 快速幂取模 ll ans = 1; a %= mod; while(n>0) { if(n&1) { ans *= a; ans %= mod; } a *= a%mod; a %= mod; n >>= 1; } return ans; } int main() { prime_table(40000); ll p, a; while(scanf("%lld %lld", &p, &a)!=EOF && a) { if(!isPrime(p) && pow_mod(a, p, p)==a%p) // WA : a^(p-1) == 1 printf("yes\n"); else printf("no\n"); } return 0; }
由于a为正整数,gcd(a,p)不一定为1,所以利用费马小定理应该判断a^p%p是否等于a%p,而不是 a^(p-1)%p==1,我少算了1次幂以为能省时间反倒让我debug了好久。。。
H题:POJ 3421 X-factor Chains 理解了题目意思后问题不大,很容易得到最长的X-链长度(素因子个数),就是被样例的数据误导了,走了些弯路探索排列总数有多少,其实就是简单的排列数问题。
#include <iostream> #include <cstdio> #include <cmath> using namespace std; int primes[1100001], cnt; bool notprime[1100001]; void prime_table(int n) { notprime[0] = notprime[1] = 1; for(int i=2;i<=n;i++) { if(!notprime[i]) primes[cnt++] = i; for(int j=0;j<cnt && i<=n/primes[j];j++) { notprime[i*primes[j]] = 1; if(i%primes[j] == 0) break; } } } long long fact(int n) { if(n==1||n==0) return 1; return fact(n-1)*n; } long long res; int diver(int n) { int ans = 0; for(int i=0;i<cnt && n>=primes[i];i++) { if(!notprime[n]) { ans++; break; } int t = 0; while(n%primes[i]==0) { n /= primes[i]; t++; } res *= fact(t); ans += t; } return ans; } int main() { prime_table(1100000); int x; while(scanf("%d", &x)!=EOF) { res = 1; int maxlen = diver(x); printf("%d %lld\n", maxlen, fact(maxlen)/res); } }
最后时G题:POJ 3292 Semi-prime H-numbers
自己改造素数表的筛法经历了各种TLE和WA,参考了别人的代码才解决,折叠吧。大佬的代码在筛选的时候就标注了H-numbers的三种类型:prime/semi-prime/非semi-prime,避免了像我开始那样找到了筛选了全部的prime表后再次两两相乘组合,然后再将结果排序,最后线性查找给定的h前面有多少semi-prime。。。真是一把辛酸泪
#include <iostream> #include <cstdio> using namespace std; const int maxn = 1000001; int H[maxn+1], sum[maxn+1]; void solve() { // 类似筛法得到素数表 for(int i=5;i<=maxn;i+=4) for(int j=5;j<=maxn && j<=maxn/i;j+=4) { // H[i]==0 表示 i为H-prime if(!H[i]&&!H[j]) H[i*j] = 1; //标记是semi-prime else H[i*j] = -1; //非semi-prime的合数 } int cnt = 0; for(int i=1;i<=maxn;i++) { if(H[i]==1) cnt++; sum[i] = cnt; } } int main() { solve(); int h; while(scanf("%d", &h)!=EOF && h) { printf("%d %d\n", c, sum[h]); } return 0; }
今天还有好多知识点还没消化完,博客开始也一点不想记录,想了半天感觉一无所获0.0 所以标题还是夸大了点Orz
最后还是提醒自己有空研究以下概念阶梯博弈、莫比乌斯反演,然和利用莫比乌斯反演以及第一天PPT上所讲解决P题:HDU 5663 Hillan and the girl
END.