A - Bi-shoe and Phi-shoe ——筛素数

题意
一个竹竿长度为p,它的score值就是比p长度小且与且与p互质的数字总数,比如9有1,2,4,5,7,8这六个数那它的score就是6。给你T组数据,每组n个学生,每个学生都有一个幸运数字,求出要求买nnn个竹子每个竹子的score都要大于或等于该学生的幸运数字,每个竹竿长度就是花费,求最小花费。
思路
ppp长度小且与且与ppp互质的数字总数就是欧拉函数的定义,就是找到第一个aaa,aaa的欧拉函数的值大于幸运值,仔细分析会发现,每次遇到质数的时候,欧拉函数就会变大,若aaa是质数,aaa的欧拉函数就是a−1a-1a−1,这也我们可以把问题转化为:在所有的素数中找到第一个aaa,aaa的欧拉函数a−1a-1a−1比幸运值大,然后将所有的找到的aaa的值a1,a2,...ama_1,a_2,...a_ma1​,a2​,...am​求和。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int Max = 1e6+10;
ll luck;
ll f[Max],bk[Max];
vector<int> prime;
void init(){memset(bk,-1,sizeof(bk));for(int i = 2; i < Max; i++) f[i] = 1;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i ){f[j] = 0;}}for(int i = 2 ; i < Max; i++){if(f[i]) prime.push_back(i-1);if(bk[i-1] == -1)bk[i-1] = i;}}
int main(){int n,cas;scanf("%d",&cas);init();for(int c = 1; c <= cas; c++){ll ans = 0;scanf("%d",&n);for(int i = 1; i <= n; i++){scanf("%d",&luck);int pos = lower_bound(prime.begin(),prime.end(),luck)-prime.begin();ans += bk[prime[pos]];//printf("%d",bk[prime[pos]]);}printf("Case %d: %lld Xukha\n",c,ans);}return 0;
}

B - Prime Independence

题意
给出n个数,找出一个最大素数独立子集,如果a=b*一个素数,那么认为a是b的一个素数乘级,如果一个集合不存在一个数是另一个数的素数乘级,那么这就是素数独立子集。
二分图匹配问题,不会

C - Aladdin and the Flying Carpet——算术基本定理的推论

题意
给一对数字 a,b ,a是一个长方形的面积,问有多少种整数的边的组合可以组成面积为a的长方形,要求最短的边不得小于b
思路
就是找到a有多少对因子满足xy = a,b<=x < y,同时有一个坑:x != y,有些博客给的解释是因为不可以是正方形。
利用N的正约数的个数为(c1+1)×(c2+1)×...×(cm+1){(c_1 + 1)\times(c_2+1)\times...\times(c_m+1)}(c1​+1)×(c2​+1)×...×(cm​+1)。
计算出所有的正约数个数 除以2得到数对的个数, 然后 减去 其中所有比b小的 x 的个数 和 x == y的个数。

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#define ll long long
using namespace std;const int Max = 1e6 + 10;
int f[Max];
vector<int> prime;
int cas;void init(){for(int i = 2; i < Max; i++) f[i] = 1;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = 0;}}for(int i = 2 ; i < Max; i++){if(f[i]) prime.push_back(i);}
}
ll solve(ll n){ll ans = 1;int cur = 0;int len = prime.size();while(n > 1 && cur < len){int m= 0;while(n % prime[cur] == 0){n /= prime[cur];m++;}ans *= (m + 1);cur++;}//如果1e6内的质数没有可以整除的,//那么证明这是一个超过1e6的指数,m = 1;if(n!=1)ans *= 2;return ans;
}
int main(){init();scanf("%d",&cas);ll a,b;for(int c = 1; c <= cas; c++){ll ans = 0;scanf("%lld %lld",&a,&b);if(b > sqrt(a)){ans = 0;}else{ll num = solve(a);ll cnt = 0;for(int i = 1; i < b; i++){if(a % i == 0) cnt++;}//printf("num = %lld  cnt = %lld\n",num,cnt);ans = num/2 - cnt;}printf("Case %d: %lld\n",c,ans);}return 0;
}
/*
10
16 2
10 2
12 2
18 3
*/

D - Sigma Function——算术基本定理

题意
将nnn使用算术基本定理分解,

定义一个新的函数Sigma Function

给一个nnn求出1~n1~n1~n有多少个σ(k)(1<=k<=n)\sigma(k)(1<= k<= n)σ(k)(1<=k<=n)的值是偶数。
思路
我们观察(p[i]^(e[i]+1) - 1)/(p[i] - 1) 这是一个等比数列和,那么可以将它变形为

p[i] ^ 0 + p[i] ^ 1 +…+ p[i] ^ e[i];

如果p[i] = 2; 则其和一定为奇数(因为从p[i] ^ 1开始后边都是偶数,而p[i] ^ 0是1 );

我们知道除2以外,素数都是奇数,所以 (p[i] ^ 0 + p[i] ^ 1 +…+ p[i] ^ e[i])要满足是奇数的话e[i]一定是偶数(两两组合还剩一个奇数);

这样如果Sigma(i)是奇数,那么连乘的每一项都是奇数。满足sigma(i)是奇数的条件,则如果i是一个平方数,那么 i 的约数和一定是奇数,如果2*i是平方数,它的约数和也是奇数,那么所有数 减去 其中Sigma(i)为奇数的个数 就是 Sigma(i)为偶数的个数。

//代码很简单,但思维却很难想
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>using namespace std;
typedef long long ll;
const int N = 1e6 + 10;int main()
{int t, p = 0;ll n;scanf("%d", &t);while(t--){p++;ll x, y;scanf("%lld", &n);x = (ll)sqrt(n);//计算n中x^2的个数y = (ll)sqrt(1.0 * n / 2);//计算n中2*x^2的个数printf("Case %d: %lld\n", p, n - x - y);}return 0;
}

E - Leading and Trailing——快速幂和对数

题意
给两个数nnn和kkk,求出nkn^knk的前三位数字和后三位数字。
思路
后三位很简单,直接快速幂 mod 一下1000,注意可能后三位可能前导0也要输出。
问题是前三位,这里有一个神奇的方法。
引入对数,将 nkn^knk 取对数变为 t=k∗log(n)t = k*log(n)t=k∗log(n),t是一个小数,将 ttt 的整数部分变成2,这样就可以将前三位变成整数,剩下的数都变成小数,然后直接强制取整就可以得到前三位的数了。

原理:因为任意一个数mmm都可以表示成10q10^q10q的形式,qqq是一个小数,qqq的整数部分就是mmm的数量级。
例如:m=150,log(m)=2.1760912590556812420812890085306m = 150,log(m) = 2.1760912590556812420812890085306m=150,log(m)=2.1760912590556812420812890085306。
mmm的数量级就是100,log(m)log(m)log(m)的整数部分就是2,所以每次把10q10^q10q中qqq的整数部分变成2,就可以把数量级降到100,这样前三位数一定属于整数部分,取整即可。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;int cas;
ll n,k;
int ans_lead,ans_trail;
//快速幂
ll qpow(ll a, ll b, ll mod){ll res = 1;while(b){if(b & 1){res = res * a % mod;}a = a * a % mod;b >>= 1;}return res;
}
int main(){scanf("%d",&cas);for(int c = 1; c <= cas; c++){scanf("%lld %lld",&n,&k);ans_trail = qpow(n,k,1000);double t = k*log10(1.0*n);//利用k*log(n)来转化int tt = floor(t);double solve = t - tt + 2;//double ans_lead = pow(10.0,fmod(k*log10(1.0*n),1));//前三位ans_lead = (ll)pow(10,solve);printf("Case %d: %3d %03d\n",c,ans_lead,ans_trail);}return 0;
}
/*
3
123456 2
*/

F - Goldbach`s Conjecture ——素数

题意:
给出几组测试数据,每组给出一个n,问n能被分成几对素数的和。
思路:
先打表,求出所有的素数。从小到大枚举所有素数,对于当前枚举的素数a,看n - a是否也是一个素数,如果是的话,就计数,求出答案。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const int Max = 1e7 + 10;
bool f[Max];
int prime[666666];
int cas,n,len = 0;void get_prime(){for(int i = 2; i < Max; i++) f[i] = 1;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = 0;}}for(int i = 2; i < Max; i++)if(f[i]) prime[len++] = i;
}
int main(){get_prime();scanf("%d",&cas);for(int c = 1; c <= cas; c++){int ans = 0;scanf("%d",&n);for(int i = 0; i < len; i++){if(prime[i] > n/2)break;if(f[n - prime[i]]){ans++;}}printf("Case %d: %d\n",c,ans);}return 0;
}

G - Harmonic Number (II)——规律 || 数论分块

题意
给出 nnn ,求和:n/1+n/2+n/3+...+n/nn/1 + n/2+n/3+...+n/nn/1+n/2+n/3+...+n/n。
思路
nnn很大无法暴力。

一、找规律解法
没有想出解法 ,网上抄的规律
1.sqrt(n)之前的数我们可以直接用for循环来求
2.sqrt(n)之后的sum += (n/i - n/(i + 1)) * i;
当sqrt(n) = n / sqrt(n)时(如第一个例子10,sum就多加了一个3),sum多加了一个sqrt(n),减去即可;

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int cas;
ll n;
int main(){scanf("%d",&cas);for(int c = 1; c <= cas; c++){ll ans = 0;scanf("%lld",&n);int m = (int)sqrt(n);for(int i = 1; i <= m; i++)ans = ans + n / i;for(int i = 1; i <= m; i++){ans = ans + (n / i - n / (i + 1)) * i;}if(m == n / m) ans -= m;printf("Case %d: %lld\n",c,ans);}return 0;
}

仔细一想符合数论分块,打一下模板

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;int cas , icas = 1;ll n;
ll solve(ll n){ll res = 0;for(ll l  = 1, r; l <= n; l = r + 1){r = n / (n / l);res += (r - l + 1) * (n / l);//printf("l = %lld  r = %lld  ans = %lld\n",l,r,(r - l + 1) * (n / l));}return res;
}
int main(){scanf("%d",&cas);while(cas--){scanf("%lld",&n);printf("Case %d: %lld\n",icas++,solve(n));}   return 0;
}

H - Pairs Forming LCM——算术基本定理

题意
求出小于nnn的数中,有多少数对的最小公倍数是nnn。
思路
由算术分解定理可以求出最大公约数和最小公倍数:
对于两个整数a,ba,ba,b
将aaa分解:a=p1e1p2e2p3e3...pmema=p_1^{e_1}p_2^{e_2}p_3^{e_3}...p_m^{e_m}a=p1e1​​p2e2​​p3e3​​...pmem​​

将bbb分解:b=p1k1p2k2p3k3...pmkmb=p_1^{k_1}p_2^{k_2}p_3^{k_3}...p_m^{k_m}b=p1k1​​p2k2​​p3k3​​...pmkm​​

GCD(a,ba,ba,b)= p1min(e1,k1)p2min(e2,k2)p3min(e3,k3)...pmmin(em,km)p_1^{min(e_1,k_1)}p_2^{min(e_2,k_2)}p_3^{min(e_3,k_3)}...p_m^{min(e_m,k_m)}p1min(e1​,k1​)​p2min(e2​,k2​)​p3min(e3​,k3​)​...pmmin(em​,km​)​

LCM(a,ba,ba,b)= p1max(e1,k1)p2max(e2,k2)p3max(e3,k3)...pmmax(em,km)p_1^{max(e_1,k_1)}p_2^{max(e_2,k_2)}p_3^{max(e_3,k_3)}...p_m^{max(e_m,k_m)}p1max(e1​,k1​)​p2max(e2​,k2​)​p3max(e3​,k3​)​...pmmax(em​,km​)​

再将GCD(a,ba,ba,b)分解:GCD(a,ba,ba,b)=p1c1p2c2p3c3...pmcm=p_1^{c_1}p_2^{c_2}p_3^{c_3}...p_m^{c_m}=p1c1​​p2c2​​p3c3​​...pmcm​​。

可知:c1=max(e1,k1),c2=max(e2,k2),...,cm=max(em,km)c_1 = max(e_1,k_1),c_2 = max(e_2,k_2),...,c_m = max(e_m,k_m)c1​=max(e1​,k1​),c2​=max(e2​,k2​),...,cm​=max(em​,km​)

如果ei=cie_i = c_iei​=ci​,那么 kik_iki​ 就可以取[0,ei][ \ 0,e_i][ 0,ei​]共ei+1e_i+1ei​+1种情况

同时,对称的来说,如果ki=cik_i = c_iki​=ci​,那么 eie_iei​ 就可以取[0,ki][ \ 0,k_i][ 0,ki​]共ki+1k_i+1ki​+1种情况。

同时因为ki=ei=cik_i = e_i = c_iki​=ei​=ci​的情况被算了两次,所以要剪掉一次,这样一来第i项就一共有2∗ci+12*c_i+12∗ci​+1种情况了。

将每一项的情况相乘,就可以得出所有可能的情况,同时因为我们计算的时候是对称的,所以我们计算出的结果是真正结果的两倍。举个栗子:当n=4n = 4n=4的时候,a=1,b=2a=1,b = 2a=1,b=2和 a=2,b=1a= 2,b=1a=2,b=1其实是一种情况,同时因为a==ba==ba==b的情况我们只算了一次,所以要加上一次a==ba==ba==b的情况,保证所有情况都出现了两次,再统一除上 2 。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const int Max = 1e7 + 10;
int cas;
bool f[Max];
vector<int> prime;
void get_prime(){for(int i = 2; i < Max; i++) f[i] = true;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i)f[j] = false;}for(int i = 2; i < Max; i++)if(f[i]) prime.push_back(i);}
ll divide(ll n){int len = prime.size();ll e ,ans = 1;//printf("len = %d\n",len);for(int i = 0 ; i < len; i++){e = 0;while(n % prime[i] == 0 && n > 1){e++;n /= prime[i];}ans *= (2 * e + 1) ;if(n == 1)break;}if(n > 1){ans *= (2 * 1 + 1) ;}return (ans + 1) / 2;}
int main(){get_prime();scanf("%d",&cas);ll n;for(int c = 1; c <= cas; c++){scanf("%lld",&n);printf("Case %d: %lld\n",c,divide(n));}return 0;
}

I - Harmonic Number ——调和级数

思路
所求的答案是一个调和级数。
直接使用上述公式

#include <bits/stdc++.h>
using namespace std;const int Max = 1e4 + 10;
const double r = 0.57721566490153286060651209;
double a[Max];
int cas,n;
void solve(){a[0] = 0;for(int i = 1; i < Max; i++){a[i] = a[i-1] + 1.0/i;}
}
int main(){scanf("%d",&cas);solve();for(int c = 1; c <= cas; c++){scanf("%d",&n);if(n < Max)printf("Case %d: %.10f\n",c,a[n]);elseprintf("Case %d: %.10f\n",c,log(n) + r + 1.0/(2*n));}return 0;
}

J - Mysterious Bacteria——算术基本定理

题意
给一个nnn,把nnn表示为bpb^pbp的形式,问最大的ppp的值是多少。
思路
算术基本定理分解 ,然后找出一个最大值。
但是,这一题有个坑, nnn 可能是负数,这就要求 ppp 在 nnn 为复数的时候要是一个奇数。
所以如果一开始分解出来的ppp是一个偶数,要一直除以2,直到变成一个奇数为止才是答案。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const int Max = 1e6 + 10;
bool f[Max];
vector<int> prime;
ll cas,n;void get_prime(){for(int i = 2; i < Max; i++) f[i] = true;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = false;}}for(int i = 2; i < Max; i++)if(f[i]) prime.push_back(i);
}
ll divide(ll n){ll c,ans = -1;int len = prime.size();for(int i = 0; i < len; i++){c = 0;while(n % prime[i] == 0 && n > 1){n /= prime[i];c++;}if(n == 1)break;}if(n > 1) c = 1;return c;
}
int main(){get_prime();scanf("%lld",&cas);ll ans = 0;for(int c = 1; c <= cas; c++){scanf("%lld",&n);if(n < 0){ans = divide(-n);while(ans % 2 == 0){ans /= 2;}}else{ans = divide(n);}printf("Case %d: %lld\n",c,ans);}return 0;
}

K - Large Division

题意
给两个大数a,ba,ba,b,问 b∣ab \ |\ ab ∣ a是否成立。
思路
正常判断是否可以整除的时候,都是取余判断,大数的时候仍然可以模拟取余来进行大数的取余运算。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define ll long long
using namespace std;const int Max = 250;
int num[Max];
int cas;
ll b;
string a;int main(){scanf("%d",&cas);for(int c = 1; c <= cas; c++){ll s = 0,len;cin>>a;scanf("%lld",&b);if(b < 0) b = -b;len = a.length();if(a[0] == '-'){for(int i = 1; i < len; i++){s = (s * 10 + (a[i] - '0')) % b;//printf("s = %d\n",s);}}else{for(int i = 0; i < len; i++){s = (s * 10 + (a[i] - '0')) % b;//printf("s = %d\n",s);}}//printf("s = %d\n",s);if(s == 0){printf("Case %d: divisible\n",c);}elseprintf("Case %d: not divisible\n",c);}return 0;
}

L - Fantasy of a Summation ——思维

题意
给出n,K,MOD,问下面这个程序中的res值为多少。

#include <stdio.h>int cases, caseno;
int n, K, MOD;
int A[1001];int main() {scanf("%d", &cases);while( cases-- ) {scanf("%d %d %d", &n, &K, &MOD);int i, i1, i2, i3, ... , iK;for( i = 0; i < n; i++ ) scanf("%d", &A[i]);int res = 0;for( i1 = 0; i1 < n; i1++ ) {for( i2 = 0; i2 < n; i2++ ) {for( i3 = 0; i3 < n; i3++ ) {...for( iK = 0; iK < n; iK++ ) {res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;}...}}}printf("Case %d: %d\n", ++caseno, res);}return 0;
}

思路
我们可以明显的算出,res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;这条语句一共运行了nkn^knk次,可重全排列总共有nkn^knk种,一共有nnn个数,所以每一个数在每一位上出现的次数为nk/n=nk−1n^k/n = n^{k-1}nk/n=nk−1,设nnn个数之和为sumsumsum,则第一位上的总和nk−1∗sumn^{k-1}*sumnk−1∗sum;又因为有kkk重循环,即k位,每一位的权都是一样的,再乘上kkk取余即可。
这里可以拿n=3,k=3n = 3,k= 3n=3,k=3来举个例子。
一共有3^3 = 27种情况,因为一共只有3个数,所以在每一位上每个数出现的次数是27 / 3 = 9次。

#include <bits/stdc++.h>
#define ll long long
using namespace std;ll cas,n,k,m,val;ll qpow(ll a,ll b,ll mod){ll res = 1;while(b){if(b & 1){res  = (res * a) % mod;}a = (a * a) % mod;b >>= 1;}return res;
}
int main(){scanf("%lld",&cas);for(int c = 1; c <= cas; c++){ll sum = 0;scanf("%lld %lld %lld",&n,&k,&m);ll ans = qpow(n,k-1,m);for(int i = 0 ; i < n; i++){scanf("%lld",&val);sum = (sum + val) % m;}ans =  (k * sum) % m * ans % m;printf("Case %d: %lld\n",c,ans);}return 0;
}

M - Help Hanzo——素数区间筛法

题意
给出a,ba,ba,b,求a,ba,ba,b中素数的个数。
思路
由于本题的a,ba,ba,b给的可能非常大,所以使用常规的筛法 + 前缀和是远远不够的。
这里用到了一个新知识点——区间筛法。

区间筛法
区间[a,b)指的是所有满足a<=x<b的整数(根据背景也可以是实数)所构成的集合。 b以内的合数的最小质因数一定不超过根号b,
如果有根号b以内的素数表的话,就可以把埃式筛法运用在[a,b)上了。 也就是说,先分别做好[2,根号b)的表和[a,b)的表,
然后从[a,根号b)的表中筛得素数得同时, 也将其倍数从[a,b)得表中划去,最后剩下的就是区间[a,b)内的素数。
——《挑战程序设计》

采用了一个技巧:将[a,b)的区间映射到了[0,b-a)上,这样就直接避免了可能存在的标记数组由于空间过大而开不了的情况。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const int Max = 1e6 + 10;
bool f[Max],vis[Max];
ll a,b,cas,ans;
vector<ll> prime;
void get_prime(){for(int i = 2; i < Max; i++) f[i] = true;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = false;}}for(int i = 2; i < Max; i++)if(f[i]) prime.push_back(i);
}
ll solve(){ll res = 0;for(ll i = 0; i <= b - a; i++) vis[i] = true;int len = prime.size();for(ll i = 0 ; i < len; i++){ll k = a / prime[i] * prime[i];if(k < a) k += prime[i];if(k < prime[i]*prime[i]) k =  prime[i]*prime[i];for(ll j = k; j <= b; j += prime[i])vis[j - a] = false;}for(ll i = 0; i <= b - a; i++)if(vis[i]) res++;if(a == 1)res--;return res;
}
int main()
{get_prime();scanf("%lld",&cas);for(int i = 1; i <= cas; i++){scanf("%lld %lld",&a,&b);ans = solve();printf("Case %d: %lld\n",i,ans);}return 0;
}

N - Trailing Zeroes (III) ——二分答案

题意
给一个数 qqq ,找出一个nnn,满足 n!n!n! 的后缀 0 的个数为 qqq 。
思路
后缀0的个数问题是一个比较经典的问题,10可以分解为2*5,由于2的个数其实是远远大于5的,所以统计5的个数就是找到后缀0的关键,还要考虑一个问题就是什么时候后缀0的个数不存在,我们来看一下前几个5的倍数,5,10,15,20,25,30 对应的后缀0的个数是1,2,3,4,6,7大家可以发现是没有5的,因为25中有两个5,所以到25的时候,4+2 = 6,直接跳过了5。

使用二分答案进行查找,每次找出1~n中5的个数,就是n!n!n!的后置0的个数。
如果最后二分答案得到结果和询问的qqq不同的话,那么就不存在答案。

下面是如何确定1~n中有几个5的过程。

    ll res = 0;while(n){res += n / 5;n /= 5;}

res就是答案, 以n == 100 来解释这个过程:

  1. n = 100时,在[1,100]内有floor(100/5)个数是可以整除5的
    ans += 100 / 5 ,ans = 20;
    n /= 5后,n = 20;
    这一步的实际含义其实是:[1,100]有多少数是5的倍数。
  2. n = 20时,在[1,20]内有floor(20/5)个数是可以整除5的
    ans += 20/5 ,ans = 24;
    这一步的实际含义其实是:[1,100]有多少数是25的倍数。

将[1,100]映射到了[1,20]上,我们可以发现在[1,20]上可以被5整除的数,就是在[1,100]上可以被25整除的数。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const ll Max = 9e8 + 10;
ll cas,q;
bool check(ll n){ll res = 0;while(n){res += n / 5;n /= 5;}//cout<<"res = "<<res<<"\n";if(res >= q)return true;elsereturn false;}
ll Erfen(ll l, ll r){while(l <= r){ll mid = (l + r) >> 1;//cout<<"l = "<<l<<" mid = "<<mid<<" r = "<<r<<"\n";if(check(mid)) r = mid - 1;else l = mid + 1;}return l;
}
int main(){scanf("%lld",&cas);for(int c = 1; c <= cas; c++){scanf("%lld",&q);ll ans = Erfen(1,Max);ll cnt = 0,t = ans;while(t){cnt += t / 5;t /= 5;}if(q != cnt){printf("Case %d: impossible\n",c);}else{printf("Case %d: %lld\n",c,ans);}}return 0;
}

O - GCD - Extreme (II)

题意

给NNN,求出∑i=1i<N∑j=i+1j<=NGCD(i,j)\displaystyle\sum_{i=1}^{i<N}\sum_{j=i+1}^{j<=N}GCD(i,j)i=1∑i<N​j=i+1∑j<=N​GCD(i,j)。

就是给出一个数 NNN,找到1~NNN中每两个数的gcd值,求这些gcd的和。

思路
找出所有互质的数,这些数的gcd就是1,找到1~n与n互质的个数就很容易想到欧拉函数,所以所有数的欧拉函数和就是所有互质的数的gcd和。

那就剩下不互质的数了。
可以在求欧拉函数的时候找到那些不互质的数的gcd。

假设a,ba,ba,b是互质的,即gcd(a,b)=1gcd(a,b) = 1gcd(a,b)=1。那么gcd(2a,2b)=2gcd(2a,2b) = 2gcd(2a,2b)=2,…,gcd(ka,kb)=kgcd(ka,kb) = kgcd(ka,kb)=k,这样
就可以通过枚举 k 来求出不互质的数的的gcd。
欧拉函数求出所有的n前面与n互质的个数,这样所有的与n互质的个数都可以像上面那样扩展,这样就可以枚举到所有不互质的数。

用一个数组ans来存下每个n和n前面的数的gcd值的和,最后再求一个前缀和就可以得到答案。
可能说的不是很清楚,来举个例子:
N=9N = 9N=9,开始枚举1到9的所有元素

枚举1,1前面没有数,ans[1] = 0;

枚举2,2的欧拉函数为1,即2前面有一个数1和2互质,
开始枚举k
k = 1,ans[1*2] += φ(2)*1 = 1; 表示1和2的gcd = 1
k = 2,ans[2*2] += φ(2)*2 = 2; 表示2和4的gcd = 2
k = 3,ans[3*2] += φ(2)*3 = 3; 表示3和6的gcd = 3
k = 4,ans[4*2] += φ(2)*4 = 4; 表示4和8的gcd = 4
k = 5,2 * 5 > 9,退出 。

枚举3,3的欧拉函数为2,3的前面有两个数1,2和3互质。
k = 1,ans[1*3] += φ(3)*1 = 2; 表示 1 和 3的gcd = 1加上 2 和 3 的gcd = 1,一共为2
k = 2,ans[2*3] += φ(3)*2 = 4; 表示 2 和 6的gcd = 2加上 4 和 6 的gcd = 2,一共为4
k = 3,ans[3*3] += φ(3)*3 = 6; 表示 3 和 9的gcd = 3加上 6 和 9 的gcd = 3,一共为6
k = 4,4 * 3 > 9,退出 。

枚举4,4的欧拉函数为2,4的前面有两个数1,2和4互质。
k = 1,ans[1*4] += φ(4)*1 = 2;
k = 2,ans[2*4] += φ(4)*2 = 4;
k = 3,3 * 4 > 9,退出 。

枚举5,5的欧拉函数为4,5的前面有四个数1,2,3,4和5互质。
k = 1,ans[1*5] += φ(5)*1 = 4;
k = 2,2 * 5 > 9,退出 。

枚举6,6的欧拉函数为2,6的前面有两个数1,6和6互质。
k = 1,ans[1*6] += φ(6)*1 = 2;
k = 2,2 * 6 > 9,退出 。

枚举7,7的欧拉函数为6,7的前面有六个数1,2,3,4,5,6和7互质。
k = 1,ans[1*7] += φ(7)*1 = 6;
k = 2,2 * 7 > 9,退出 。

枚举8,8的欧拉函数为4,8的前面有四个数1,3,5,7和8互质。
k = 1,ans[1*8] += φ(8)*1 = 4;
k = 2,2 * 8 > 9,退出 。

枚举9,9的欧拉函数为6,7的前面有六个数1,2,4,5,7,8和9互质。
k = 1,ans[1*9] += φ(9)*1 = 6;
k = 2,2 * 9 > 9,退出 。

累加前的ans[1~9]为
0 1 2 4 4 9 6 12 12
之后求前缀和即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const ll Max = 4e6 + 10;
ll n;
ll phi[Max], ans[Max];ll gcd(ll a, ll b) {return b = 0 ? 1 : gcd(b, a % b);
}
void get_euler() {for(ll i = 1; i < Max; i++) phi[i] = i;for(ll i = 2; i < Max; i++) {if(phi[i] == i) {for(ll j = i; j < Max; j += i) {phi[j] = phi[j] / i * (i - 1);}}for(ll j = 1; j * i < Max; j++) {ans[j * i] += phi[i] * j;}}for(int i = 1; i <= 9; i++)cout<<ans[i]<<" ";for(int i = 1; i < Max; i++)ans[i] += ans[i - 1];}
int main() {get_euler();while(scanf("%lld", &n) && n) {printf("%lld\n", ans[n]);}return 0;
}

R - 青蛙的约会——扩展欧几里得求线性同余方程

此题是扩展欧几里得的经典题目
题意:
两只青蛙A和B在一条首尾相接的线段上,线段长为L,两蛙要见面,方向相对,A的起始位置是X,每次可以跳m米,B的起始位置是Y,每次可以跳n米,问什么它们跳几次可以见面。
思路:
求什么我们就设什么,设两只蛙,跳了T步相遇,此时A的坐标是X + mT,B的坐标就是Y + nT,当A和B的坐标的差值是L的倍数的时候,两只蛙可以相遇。
那么我们可以得到一个方程:
X+mT−(Y+nT)=LP(P∈Z)X+mT - (Y+nT) = LP(P\ \in Z)X+mT−(Y+nT)=LP(P ∈Z)即(n−m)T+LP=X−Y,L>0(n-m)T + LP = X-Y,L>0(n−m)T+LP=X−Y,L>0

这样就可以使用扩展欧几里得算法来解题了,T和P就是待求的未知元。

  • 求出 (n−m){(n- m)}(n−m) 和 l{l}l 的最大公约数ggg,如果ggg不能整除X−Y{X-Y}X−Y,则无解。

  • 如果可以整除,设a′=(n−m)/g,b′=L/g,c′=(X−Y)/g{a^{'} = (n-m)/g,b^{'} = L/g ,c^{'} = (X-Y)/g}a′=(n−m)/g,b′=L/g,c′=(X−Y)/g。 先用Exgcd(a′,b′,x0,y0a^{'},b^{'},x_0,y_0a′,b′,x0​,y0​)求出a′x′+b′y′=1{a^{'}x^{'} + b^{'}y^{'} = 1}a′x′+b′y′=1的一组特解,也就是a′c′x′+b′c′y′=c′{a^{'} c^{'}x^{'} + b^{'}c^{'}y^{'}= c^{'}}a′c′x′+b′c′y′=c′的一组特解,(x0,y0){(x_0,y_0)}(x0​,y0​)。

  • 接着就可以求出原方程的一组特解t0=c′x′{t_0 = c^{'}x^{'}}t0​=c′x′

  • 通过求出来的特解t0{t_0}t0​,求出通解:t=t0+b′k=x′×(X−Y)/g+L/g∗k。(k∈Z){t = t_0 +b^{'}k = x^{'}\times(X-Y)/g\ +\ L/g*k}。(k \in Z)t=t0​+b′k=x′×(X−Y)/g + L/g∗k。(k∈Z)

  • 找出最小的正整数解ans=(t%b′+b′)%b′{ans = (t \ \%\ b^{'}+b^{'})\ \%\ b^{'} }ans=(t % b′+b′) % b′

#include <bits/stdc++.h>
#define ll long long
using namespace std;ll exgcd(ll a,ll b,ll &x,ll &y) {if(b == 0) {x = 1;y = 0;return a;}ll t = exgcd(b,a%b,x,y);ll x1 = x,y1 = y;x = y1;y = x1 - a / b * y1;return t;
}
int main() {ll a0,b0,c0,g,m,n,X,Y,L,t0,x0,y0,ans;scanf("%lld%lld%lld%lld%lld",&X,&Y,&m,&n,&L);g = exgcd(n-m,L,x0,y0);if((X-Y)%g != 0 || m == n)printf("Impossible\n");else {a0 = (n-m)/g;b0 = L/g;c0 = (X-Y)/g;exgcd(a0,b0,x0,y0);t0 = c0*x0;//printf("g = %d\nb0 = %d \nt0 % b0 = %lld \n",g,b0,t0 % b0);ans = (t0 % b0 + b0)%b0;printf("%lld\n",ans);}return 0;
}

S - C Looooops ——扩展欧几里得求线性同余方程

题意
在一个2k2^k2k的处理器上运行下面的程序:

for (variable = A; variable != B; variable += C)statement;

给出A,B,C,k,问statement;执行了多少次。
思路
位数就是我们取模的模数,求解一阶线性同余方程。
设一共运行了X次。
X * C ≡\equiv≡ (B - A)( mod 2k2^k2k)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;ll exgcd(ll a, ll b, ll &x, ll &y){if(b == 0){x = 1;y = 0;return a;}ll t = exgcd(b, a % b,x,y);ll x1 = x,y1 = y;x = y1;y = x1 - a / b * y1;return t;
}
int main(){ll a,b,c,k,x,y,ta,tb,tc,x0;while(scanf("%lld %lld %lld %lld",&a,&b,&c,&k) && a + b + c + k != 0){ta = c;tb = 1ll << k;ll g = exgcd(ta,tb,x,y);tc = b - a;if(tc % g != 0) printf("FOREVER\n");else{x0 = tc / g * x % tb;ll ans = (x0 % (tb / g) + tb / g ) % (tb / g);printf("%lld\n",ans);}}return 0;
}

U - Primes ——素数筛

题意
判断一个是不是素数,同时规定1和2不是素数。
思路
打表判断素数。

#include <bits/stdc++.h>
#define ll long long
using namespace std;const ll Max = 2e4;
bool f[Max];void get_prime(){for(ll i = 2ll; i < Max; i++) f[i] = true;for(ll i = 2ll; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = false;}}f[1] = false;f[2] = false;
}
int main(){int cnt = 1,n;get_prime();while(scanf("%d",&n) != EOF && n > 0){if(f[n])printf("%d: yes\n",cnt++);elseprintf("%d: no\n",cnt++);}return 0;
}

V - Maximum GCD

题意
给m个数,找出这m个数中两两配对最大的gcd值。
思路
用字符串读取后分割出数字,然后直接暴力求出每两个的gcd值,比较即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <sstream>
#define ll long long
using namespace std;vector<ll> a;
ll gcd(ll a,ll b){return b == 0ll ? a : gcd(b, a % b);
}
int main(){ll n;scanf("%lld",&n);getchar();string str;while(n--){getline(cin,str);ll maxx = -1;stringstream ss(str);a.clear();int tmp;while(ss >> tmp){a.push_back(tmp);}ll size_ = a.size();for(ll i = 0ll ; i < size_ - 1ll; i++){for(ll j = i + 1ll; j < size_; j++){maxx = max(maxx,gcd(a[i],a[j]));}}printf("%lld\n",maxx);}return 0;
}

W - Prime Time

题意
给一个区间[L,R],区间中有数nnn满足,n2+n+41n^2+n+41n2+n+41是一个质数。问区间中满足条件的数,占区间的百分之几?
思路
枚举每个 i 如果 i 满足条件,则直接将dp[i]设为1,然后进行前缀和相加,求出区间内的个数的时候使用前缀的性质dp[R] - dp[L-1]即可。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;const int Max = 1e4 + 10;
int dp[Max];ll formula() {for(ll i = 0 ; i < Max; i++) {ll num = i * i + i + 41;ll flag = 0;for(ll j = 2; j * j <= num; j++) {if(num % j == 0) {flag = 1;break;}}if(!flag)//素数dp[i] = 1;}for(ll i = 1; i <= Max; i++)dp[i] += dp[i - 1];
}
int main() {double ans,fenzi,fenmu;int a,b;formula();while(scanf("%d %d", &a, &b) != EOF) {fenmu = b - a + 1;if(a == 0) {a = 1;fenzi = (dp[b] - dp[a - 1] + 1);}else fenzi = (dp[b] - dp[a - 1] );ans = fenzi / fenmu;printf("%.2f\n", ans * 100.0 + 1e-8);}return 0;
}

X - Farey Sequence

题意
每个数m可以和m前面并且和m互质的m组成分数。例如:
F2 = {1/2}
F3 = {1/3, 1/2, 2/3}
F4 = {1/4, 1/3, 1/2, 2/3, 3/4}
F5 = {1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5}
给一个数n,求出Fn中的元素个数。
思路
欧拉函数的应用,打表求出欧拉函数 然后使用前缀和。

#include <cstdio>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;const int Max = 1e6 + 10;
ll phi[Max];
bool f[Max];
vector<ll> prime;void get_prime(){for(ll i = 2; i < Max; i++) f[i] = true;for(ll i = 2; i < Max; i++){for(ll j = i * i; j < Max; j += i){f[j] = false;}}for(ll i = 2; i < Max; i++){if(f[i]) prime.push_back(i);}
}
void get_phi(){for(ll i = 1; i < Max; i++) phi[i] = i;for(ll i = 2; i < Max; i++){if(phi[i] == i){for(ll j = i ; j < Max; j += i){phi[j] = phi[j] / i * (i - 1);}}}phi[1] = 0;for(ll i = 2; i < Max; i++)phi[i] += phi[i - 1];
}
int main(){ll n;get_phi();while(scanf("%lld",&n) != EOF && n){printf("%lld\n",phi[n]);}return 0;
}

Y - The Super Powers

题意
一个数可以分解成至少两个数的幂形式,就把这个数叫超级幂。例如:
64=82and64=43,6464 = 8^2 \ and \ 64 = 4^3,6464=82 and 64=43,64就是一个超级幂。
思路
底数在[2,1<<16]内,指数是64以内的合数。底数为a的幂不会溢出的上限为ceil(64/((log(a)/log(2)))-1。
指数是合数 就可以保证可以分解成多个数的幂形式。
例如:
64=26,6=2∗364 = 2^6,6 = 2*364=26,6=2∗3
64=(22)3=43=(23)2=8264 = (2^2)^3=4^3 = (2^3)^2 =8^264=(22)3=43=(23)2=82.

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <cmath>
#define ll unsigned long long
using namespace std;bool f[70];
vector<ll> Compos;int len;void init(){for(ll i = 2; i <= 64; i++) f[i] = 0;for(ll i = 2; i <= 64; i++){for(ll j = i * i;  j <= 64; j += i)f[j] = 1;}for(int i = 4; i <= 64; i++)if(f[i]) Compos.push_back(i);len = Compos.size();
}
void solve(){set<ll> ans;ll limit1 = 1 << 16;ll t;ans.insert(1);for(ll i = 2; i < limit1; i++){ll limit2 = ceil(64 * log(2) / log(i)) - 1;ll tmp = i * i * i * i;ans.insert(tmp);for(ll j = 1; Compos[j] <= limit2 ; j++){tmp *= Compos[j] - Compos[j - 1] == 1 ? i : i * i;ans.insert(tmp);}}for(set<ll>::iterator it = ans.begin(); it != ans.end(); it++){printf("%llu\n",*it);}
}
int main(){init();solve();return 0;
}

[kuangbin带你飞]专题十四 数论基础相关推荐

  1. [kuangbin带你飞]专题十二 基础DP1 题解+总结

    kuangbin带你飞:点击进入新世界 总结: 简单dp,最近在做,持续更新. 文章目录 总结: 1.Max Sum Plus Plus 2.Ignatius and the Princess IV ...

  2. [kuangbin带你飞]专题十二 基础DP1

    A - Max Sum Plus Plus (HDU 1024) 题意:将n个数取m段且不相交,求m段数字和最大值: dp[i][j]:前i个数字分成j段的最大值. 边界dp[0][0] = 0; d ...

  3. [kuangbin带你飞]专题十二 基础DP1 C - Monkey and Banana HDU - 1069

    C - Monkey and Banana HDU - 1069 题目链接:https://vjudge.net/contest/68966#problem/C 题目: A group of rese ...

  4. kuangbin带你飞专题合集

    题目列表 [kuangbin带你飞]专题一 简单搜索 [kuangbin带你飞]专题二 搜索进阶 [kuangbin带你飞]专题三 Dancing Links [kuangbin带你飞]专题四 最短路 ...

  5. kuangbin带你飞 专题1-23 题单

    kuangbin大神,对于打过ACM比赛的ACMer,无人不知无人不晓. 在此,附上vjudge平台上一位大神整理的[kuangbin带你飞]专题目录链接. [kuangbin带你飞专题目录1-23] ...

  6. “kuangbin带你飞”专题计划——专题十四:数论基础

    写在前面 1.目前还没啥写的.开始时间:2021-05-13(其实博客上看得到该博客创建时间的) 2.上一个专题刷的是网络流(博客总结),属于第一次接触.本来想的是一周特别高效,然后一周略划水,结果是 ...

  7. (2021-07-14~)“kuangbin带你飞”专题计划——专题十三:基础计算几何

    目录 前言 参考博客 自己总结的东西: 难度判断? 题目 1.[TOYS POJ - 2318 ](解决) 2.[Toy Storage POJ - 2398 ](解决) 3.[Segments PO ...

  8. [kuangbin带你飞]专题五 并查集 题解+总结

    kuangbin带你飞:点击进入新世界 总结: 本人算是初学者中的初学者,欢迎交流~ 并查集的接触过的不多,大概只有普通并查集,带权并查集,种族并查集,传说中的可持续化并查集只是听说过还没有接触,不过 ...

  9. [kuangbin带你飞]专题四 做题顺序与题解 【最短路练习】

    随便说点: 博主正在刷kuangbin专题的题目,初学者,没接触过什么算法,刷题的初衷是备战蓝桥杯,后来发现了算法资料大多是针对acm的,挑选kuangbin专题入门也是如此,毕竟这样分类看起来能达到 ...

最新文章

  1. spring-mvc注解
  2. 8000字讲清楚从0到1搭建电商商品中心(建议收藏)
  3. Uploadify3.2中文提示
  4. 函数、指针、数组的组合 及结构体和共用体
  5. 企业网站常用中英文对照表
  6. c语言黄建灯第七章答案,c语言实训大纲.doc
  7. 计算机视觉论文-2021-07-07
  8. JVM性能优化之JVM调优
  9. 一个人、一场梦、一座空城、一生心疼
  10. vb脚本实现电脑定时关机操作
  11. 使用MD5加密的登陆demo
  12. 智能管家App kotlin版(2)——工具类封装与首页引导页开发
  13. win7计算机变成英文,我的win7开机选项变成了英文怎么处理
  14. Oracle 表空间详解(tablespace)
  15. matlab scatter 散点图画法
  16. 项目团队效能的影响因素及其生成关系模型
  17. 戴尔服务器盘柜盘故障分析
  18. b丅151组成的充电器电路_【图】可控硅BT136中文资料(参数,引脚及应用电路)
  19. 安装完svn客户端后在bin目录中没有svn.exe文件
  20. 迅雷将启动收费下载服务 因版权问题屡次被告

热门文章

  1. 山东省人力资源和社会保障厅容灾及备份项目要求
  2. 点击查看更多进行分页,uniapp,分页请求
  3. 化工热力学(第三版)答案陈
  4. 职业最低谷时的样子——北漂18年(55)
  5. 中国大学MOOC慕课视频字幕获取方法
  6. 计算机主机ip地址题,(完整版)IP地址划分测试题
  7. 简易设置动态桌面壁纸的方法
  8. 组织结构中岗位和角色的体系
  9. 百度官网认证多少钱? 百度官网认证作用和价值
  10. iPad投影到Windows电脑屏幕上的方法【亲测,均为免费软件,均可用,仅供学习使用】