A - Mathematically Hard——欧拉函数的简单应用
题意
求1~n的欧拉函数的平方的和。
思路
打表求出5e6内的欧拉函数,然后再求平方的前缀和。
需要注意的两点:
1.要用unsigned long long ,只用long long的话范围不够。
2.注意内存,开两个数组似乎就会爆空间。
先用了线性筛法,一直疯狂爆内存,换了埃式筛法,就过了 …我好南啊。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll unsigned long long
using namespace std;
const int N = 5e6 + 10;
ll phi[N] = {
0};
int cas,a,b;
void euler1(int n) {
for(int i = 2; i < n; i++) phi[i] = i;
for(int i = 2; i < n; i++) {
if(phi[i] == i) {
for(int j = i; j < n; j += i) {
phi[j] = phi[j] / i * (i - 1);
}
}
}
for(int i = 2 ; i < n; i++){
phi[i] = (ll)(phi[i-1] + phi[i] * phi[i]);
}
}
int main(){
euler1(N-5);
scanf("%d",&cas);
int cnt = 0;
while(cas--){
scanf("%d %d",&a,&b);
printf("Case %d: %llu\n",++cnt,phi[b] - phi[a-1]);
}
return 0;
}
B - Ifter Party——分解因子
题意
有C个人,然后给他们P个食物,每个人吃Q个,然后剩下L个,给定P和L,Q > L求Q可能的情况。
思路
本题就是分解Q-L的因子,升序输出其中大于L的因子,分解因子的时候要特别注意到因子刚好是开方的情况。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 2e5;
int cas;
ll p, l;
ll ans[N], cnt = 0;
void divide(int n) {
for(int i = 1; i <= sqrt(n); i++) {
if(n % i == 0) {
if(i > l)
ans[cnt++] = i;
if(n / i > l && i * i != n)
ans[cnt++] = n / i;
}
}
}
int main() {
scanf("%d", &cas);
for(int icas = 1; icas <= cas; icas++) {
cnt = 0;
scanf("%lld %lld", &p, &l);
divide(p - l);
printf("Case %d:", icas);
if(cnt == 0) {
printf(" impossible\n");
} else {
sort(ans,ans + cnt);
for(int i = 0 ; i < cnt; i++)
printf(" %lld", ans[i]);
puts("");
}
}
return 0;
}
C - Eid
题意
求给定的 n n n个数的LCM。
思路
利用唯一分解定理的一个性质:
令: a = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p m c m a = p_1^{c_1}*p_2^{c_2}*...*p_m^{c_m} a=p1c1∗p2c2∗...∗pmcm
b = p 1 d 1 ∗ p 2 d 2 ∗ . . . ∗ p m d m b = p_1^{d_1}*p_2^{d_2}*...*p_m^{d_m} b=p1d1∗p2d2∗...∗pmdm
则 l c m ( a , b ) = p 1 m a x ( c 1 , d 1 ) ∗ p 2 m a x ( c 2 , d 2 ) ∗ . . . ∗ p m m a x ( c m , d m ) lcm(a,b) = p_1^{max(c_1,d_1)}* p_2^{max(c_2,d_2)}*...* p_m^{max(c_m,d_m)} lcm(a,b)=p1max(c1,d1)∗p2max(c2,d2)∗...∗pmmax(cm,dm)
所以直接找出每个质数因子的最高次幂,然后求所有每个质因子最高次幂的乘积即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<sstream>
#include<cmath>
#define ll long long
using namespace std;
const int N = 10010;
int m, cas, a,len;
int prime[N], prime_tot = 0, p[N], c[N],tot = 0,ans[N];
bool prime_tag[N],bk[N];
void get_prime() {
for(int i = 2 ; i < N; i++) prime_tag[i] = true;
for(ll i = 2; i < N; i++) {
for(int j = i * i; j < N; j += i) {
prime_tag[j] = false;
}
}
for(int i = 2; i < N; i++)
if(prime_tag[i]) prime[prime_tot++] = i;
}
void mul(int x) {
int c=0;
for(int i=0; i<len; i++) {
int k=ans[i]*x+c;
c=k/10;
ans[i]=k%10;
if(i==len-1&&c) {
//ans[len]=c;
len++;
}
}
}
void divide(int n) {
for(int i = 0 ; i < prime_tot && prime[i] <= n; i++) {
int tmp = 0;
if(n % prime[i] == 0) {
if(!bk[prime[i]]){
p[tot++] = prime[i];
bk[prime[i]] = true;
}
while(n % prime[i] == 0 && n > 1) {
tmp++;
n /= prime[i];
}
c[prime[i]] = max(c[prime[i]], tmp);
if(n == 1) break;
}
}
if(n > 1) {
p[tot++] = n;
c[n] = max(c[n], 1);
}
}
int qpow(int a, int b) {
int ret = 1;
while(b) {
if(b & 1) {
ret *= a;
}
a *= a;
b >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
get_prime();
int icas = 1;
cin >> cas;
//scanf("%d", &cas);
while(cas--) {
tot = 0;
memset(p, 0, sizeof(p));
memset(c, 0, sizeof(c));
memset(bk, 0, sizeof(bk));
memset(ans, 0, sizeof(ans));
cin >> m;
//scanf("%d",&m);
for(int i = 0; i < m; i++) {
cin >> a;
//scanf("%d",&a);
divide(a);
}
ans[0] = 1;
len = 1;
for(int i = 0; i < tot; i++) {
int num = qpow(p[i],c[p[i]]);
mul(num);
}
cout << "Case " << icas++ << ": " ;
for(int j = len - 1; j >= 0; j--)
cout << ans[j];
cout << "\n";
}
return 0;
}
/*
3
5
1 2 3 5 7
3
2 20 10
4
5 6 30 60
*/
D - Trailing Zeroes (I) ——唯一分解定理推论求因数个数
题意
给一个数,求出除1以外的因子的个数。
思路
利用唯一分解定理的一个推论:
N N N的正约数个数为( ∏ \prod ∏表示连乘)
( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c m + 1 ) = ∏ i = 1 m ( c i + 1 ) (c_1+1)*(c_2+1)*...*(c_m+1)=\displaystyle\prod^m_{i=1} (c_i+1) (c1+1)∗(c2+1)∗...∗(cm+1)=i=1∏m(ci+1)
求出 N N N的约数个数以后再减去1即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
ll cas,n;
int prime[N],prime_tot = 0;
bool prime_tag[N] = {
0};
void get_prime() {
for(int i = 2; i < N; i++) {
if(!prime_tag[i]) {
prime[prime_tot++] = i;
}
for(int j = 0 ; j < prime_tot && i * prime[j] < N; j++) {
prime_tag[i * prime[j]] = true;
if(i % prime[j] == 0)
break;
}
}
}
ll solve(ll n) {
ll res = 1;
for(int i = 0 ; i < prime_tot && prime[i] * prime[i] <= n; i++) {
if(n % prime[i] == 0) {
ll c = 0;
while(n % prime[i] == 0 && n > 1) {
c++;
n /= prime[i];
}
res = res * (c + 1);
if(n == 1)
break;
}
}
if(n > 1)
res = res * 2;
return res - 1;
}
int main() {
int icas = 1;
get_prime();
scanf("%lld",&cas);
while(cas--) {
scanf("%lld",&n);
printf("Case %d: %lld\n",icas++,solve(n));
}
return 0;
}
E - Intelligent Factorial Factorization——唯一分解定理分解质因子
题意
给一个 N N N,将 N ! N! N!分解质因子。
思路
将数据范围内所有的数分解质因子,将质因子和幂储存再数组里,然后求一个前缀和就等于是将阶乘分解质因子。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 110;
int p[N][N], c[N][N] = {
0}, prime[N],ans[N][N] = {
0};
int p_cnt = 0, prime_tot = 0;
bool prime_tag[N];
int n, cas, icas = 1;
void get_prime() {
for(int i = 1; i < N; i++) prime_tag[i] = true;
for(int i = 2; i < N; i++) {
for(ll j = i * i ; j < N; j += i)
prime_tag[j] = false;
}
for(int i = 2; i < N; i++)
if(prime_tag[i]) prime[prime_tot++] = i;
}
void divide(int m) {
p_cnt = 0;
int up = m;
for(int i = 0 ; i < prime_tot && prime[i] * prime[i] <= up; i++){
if(m % prime[i] == 0){
p[up][p_cnt++] = prime[i];
c[up][prime[i]] = 0;
while(m % prime[i] == 0 && m > 1){
c[up][prime[i]]++;
m /= prime[i];
}
if(m == 1)
break;
}
}
if(m > 1)
p[up][p_cnt++] = m,c[up][m] = 1;
}
void solve(){
for(int i = 2; i <= 100; i++){
for(int j = 0; j < prime_tot; j++){
ans[i][prime[j]] = ans[i - 1][prime[j]] + c[i][prime[j]];
}
}
}
int main() {
get_prime();
for(int i = 2; i <= 100; i++)
divide(i);
solve();
scanf("%d", &cas);
while(cas--) {
scanf("%d", &n);
printf("Case %d: %d = ",icas++,n);
int first = 1;
for(int i = 0 ; i < prime_tot; i++){
if(ans[n][prime[i]]){
if(first){
printf("%d (%d)",prime[i],ans[n][prime[i]]);
first = 0;
}else{
printf(" * %d (%d)",prime[i],ans[n][prime[i]]);
}
}
}
printf("\n");
}
return 0;
}
F - Digits of Factorial ——对数性质
题意
n ! n! n! 在 m m m 进制下有几位数
思路
首先可以发现一个规律,对于一个数 K K K,它在 m m m 进制下的数位个数就是 l o g m K log_mK logmK。
那么我们要求的答案就是 a n s = l o g m n ! ans =log_m\ n! ans=logm n!。
根据对数的运算规律
a n s = l o g m n ! = l o g m 1 + l o g m 2 + . . . + l o g m n ans =log_m\ n!=log_m1+log_m2+...+log_mn ans=logm n!=logm1+logm2+...+logmn
a n s = l o g ( 1 ) + l o g ( 2 )