A - The Euler function 来源:HDU 2824
计算[a,b]区间内的整数的欧拉函数值,需要掌握单个欧拉函数和函数表的使用。
#include <iostream> #include <cstdio> using namespace std; const int MAX_N = 3000010; typedef long long ll; int phi[MAX_N]; // ll sum_phi[MAX_N]; 若使用前缀和累加,会爆内存(MLE) void phi_table(int n) { // 计算得到n以内的欧拉函数表,参考蓝书P121 int i, j; for(i=1;i<=n;i++) phi[i] = i; for(i=2;i<=n;i+=2) phi[i] /= 2; for(i=3;i<=n;i+=2) { if(phi[i]==i) { for(j=i;j<=n;j+=i) phi[j] = phi[j] / i * (i - 1); } } } int main() { phi_table(3000000);
// for(int i=1;i<=3000000;i++)
// sum_phi[i] = sum_phi[i-1] + phi[i];
// int a, b;
// while(cin>>a>>b)
// printf("%lld
", sum_phi[b]-sum_phi[a-1]);
int a, b; while(cin>>a>>b) { ll sum = 0; for(int i=a;i<=b;i++) sum += phi[i]; printf("%lld ", sum); } return 0; }
B - Divisors 来源:POJ 2992
求组合数C(n, k)的因子个数,0 ≤ k ≤ n ≤ 431。
打表可得431以内的素数只有83个,由于C(n, k) = n!/(k!*(n-k)!) = n*(n-1)*…*(n-k+1)/k!,开始的直观想法是求出分子上的每个素数因子的总个数,再减去分母上出现的每个素数因子的总个数,正约数的个数即为(a1+1)*(a2+1)*…*(an+1)。然而多次提交优化仍然超时。。。最后参考https://www.cnblogs.com/zxhyxiao/p/8026280.html,学到了计算N!中某个素因子个数的计算方法,结合网上的做法,终于AC。
求N!中素因子p的个数,也就是N!中p的幂次
公式为:cnt=[n/p]+[n/p^2]+[n/p^3]+...+[n/p^k]
核心代码:
int cnt = 0; while(N)
{ cnt += N/p; N /= p; }
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int p[200], cnt, num[440]; bool notprime[440]; void prime_table(int n) { // 筛素数 notprime[0] = notprime[1] = 1; int i, j; for(i=2;i<=n;i++) { if(!notprime[i]) { num[i] = cnt; p[cnt++] = i; } for(j=0;j<cnt && i*p[j]<n;j++) { notprime[i*p[j]] = 1; if(i%p[j]==0) break; } } } int main() { prime_table(431); // for(int i=0;i<cnt;i++) cout<<p[i]<<endl; // cout<<cnt<<endl; int n, k; while(scanf("%d %d", &n, &k)!=EOF) { long long ans = 1; for(int i=0;i<cnt && p[i]<=n;i++) { int N = n, cnt = 0; while(N) { cnt += N/p[i]; N /= p[i]; } N = k; while(N) { cnt -= N/p[i]; N /= p[i]; } N = n-k; while(N) { cnt -= N/p[i]; N /= p[i]; } ans *= (cnt+1); } printf("%lld ", ans); } return 0; }
C - Longge's problem 来源:POJ 2480
计算∑gcd(i, N), 1<=i<=N
简单推导可得所求结果为∑i*phi(n/i), i|n.由于n的上限太大(1e18),书上打表的办法失效,一时陷入困境。再次百度参考https://www.cnblogs.com/flipped/p/5690123.html才茅塞顿开,直接采用O(√n)求单个欧拉函数值。需要特别注意注释处,最开始好几次TLE都是因为i*i结果为int类型溢出变为负数,出现了死循环。
#include <iostream> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; ll euler(int x) { // 计算欧拉函数phi(x) int res=x, m = (int)sqrt(x+0.5); for(int i=2; i<=m; i++) if(x%i==0) { res = res/i*(i-1); while(x%i==0) x/=i; } if(x>1) res = res/x*(x-1); return res; } int main() { ll n, ans; while(scanf("%lld", &n)!=EOF) { ans = 0; int i; for(i=1;i<n/i;i++) // for(i=1;(ll)i*i<n;i++) { if(n%i==0) ans += i*euler(n/i) + n/i*euler(i); } if(i*i==n) ans += i*euler(i); printf("%lld ", ans); } }
E - Happy 2006 来源:POJ 2773
计算与m互质的第K大的正整数,m (1 <= m <= 1000000), K (1 <= K <= 100000000)
直接上AC代码:
#include <iostream> #include <cstdio> #include <cmath> using namespace std; int gcd(int a,int b) { if(!b) return a; return gcd(b,a%b); } int euler(int x) { int res=x, m = (int)sqrt(x+0.5); for(int i=2; i<=m; i++) if(x%i==0) { res = res/i*(i-1); while(x%i==0) x/=i; } if(x>1) res = res/x*(x-1); return res; } int main() { int m, k; while(scanf("%d %d", &m, &k)!=EOF) { int sum = euler(m); int t = k % sum, tt = 0, i; if(t==0) t = sum; //注意整除的处理 for(i=1;i<m;i++) { if(gcd(i, m)==1) tt++; if(tt==t) break; } printf("%d ", i+(k-1)/sum*m); } }
貌似以上做法的欧拉函数显得多余,直接用gcd判断m以内全部互质的整数,总个数即为sum。
D - GCD & LCM Inverse 来源:POJ 2429
最后AC的D题,参考了网上用dfs得到数组中最接近sqrt(N)的部分元素之积。晚上讲题解时滕佬指明一定要用Miller Rabin算法,直接在其模板基础上修改而来。代码十分长而且显得有些杂乱,日后再做整理吧~~
#include <stdio.h> #include <time.h> #include <stdlib.h> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; ll box[1000]; ll N, T, num; ll min(ll a , ll b) { return (a < b) ? a : b; } ll gcd(ll a , ll b) { while(b) { ll t = a; a = b; b = t % b; } return a; } ll tim(ll a , ll b , ll mo) { a %= mo; b %= mo; ll ans = 0; while(b) { if(b & 1) { ans += a; if(ans > mo) ans -= mo; } b >>= 1; a <<= 1; if(a > mo) a -= mo; } return ans; } ll po(ll x , ll n , ll mo) { x %= mo; ll ans = 1; while(n) { if(n & 1) ans = tim(ans , x , mo); x = tim(x , x , mo); n >>= 1; } return ans; } bool tes(ll n , ll d , ll t , ll mo) { ll y = po(t , n , mo); ll i; for(i = 0 ; i < d ; i++) { ll no = tim(y , y , mo); if(y != mo - 1 && y != 1 && no == 1) { return false; } y = no; } if(y != 1) { return false; } return true; } bool MR(ll n) { if(n < 2) return false; if(n == 2 || n == 3) return true; if((n & 1) == 0 || n % 3 == 0) return false; ll b = n - 1, t = 0; while((b & 1) == 0) { b >>= 1; t++; } ll i, ty[8] = {2 , 3 , 7 , 61 , 24251 , 657 , 6436 , 123}; for(i = 0 ; i < 8 ; i++) { if(!tes(b , t , ty[i] , n) && ty[i] < n) { return false; } } return true; } ll Ro(ll n , ll c) { srand(time(NULL)); ll x0 = rand() % (n - 1) + 1; ll x = x0, y = x0, k = 0, i = 1, d = 1; while(d == 1) { k++; x = (tim(x , x , n) + c) % n; d = gcd(-1 * min(x - y , y - x) , n); if(d > 1 && d < n) return d; if(d == n) return -1; if(k == i) { y = x; i <<= 1; } } } void fj(ll n) { if(n == 1) return; if(MR(n)) { box[num++] = n; return; } ll p = -1, c = 107; while(p == -1) p = Ro(n , c--); fj(n / p); fj(p); } ll fact[100], common; int cnt = 0; void dfs(ll now, int n) { if(now>sqrt(N)) return; common = max(common, now); for(int i=n;i<=cnt;i++) dfs(now*fact[i], i+1); } int main() { ll lcm, gcd; while(scanf("%lld %lld", &gcd, &lcm)!=EOF) { N = lcm / gcd; MR(N); num = 0; fj(N); sort(box, box+num); // for(int i=0;i<num;i++) // printf("%lld ", box[i]); cnt = 0; for(int i=1;i<100;i++) fact[i] = 1; fact[0] = box[0]; for(int i=1;i<num;i++) { if(box[i]==box[i-1]) fact[cnt] *= box[i]; else fact[++cnt] = box[i]; } //for(int i=0;i<=cnt;i++) printf("%lld ", fact[i]); common = 1; dfs(1, 0); printf("%lld %lld ", common*gcd, lcm/common*gcd); } return 0; }
END.