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
题意
求给定的 个数的LCM。
思路
利用唯一分解定理的一个性质:
令:
则
所以直接找出每个质数因子的最高次幂,然后求所有每个质因子最高次幂的乘积即可。
#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以外的因子的个数。
思路
利用唯一分解定理的一个推论:
的正约数个数为(
表示连乘)
求出 的约数个数以后再减去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——唯一分解定理分解质因子
题意
给一个 ,将 分解质因子。
思路
将数据范围内所有的数分解质因子,将质因子和幂储存再数组里,然后求一个前缀和就等于是将阶乘分解质因子。
#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 ——对数性质
题意
在 进制下有几位数
思路
首先可以发现一个规律,对于一个数
,它在
进制下的数位个数就是
。
那么我们要求的答案就是 。
根据对数的运算规律
我们可以通过预处理来求出所有的 ,求出一个前缀和,在算答案的时候直接除以 就可以了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
int n,cas,base;
double log_[N];
void init() {
log_[0] = 0;
for(int i = 1; i < N; i++) {
log_[i] = log_[i - 1] + log(i);
}
}
int main() {
init();
scanf("%d",&cas);
int icas = 1;
while(cas--) {
scanf("%d %d",&n,&base);
double ans = log_[n];
if(n == 0) {
ans = 0;
} else {
ans /= log(base);
}
printf("Case %d: %lld\n",icas++,(ll)(ans+1));
}
return 0;
}
G - Combinations——Lucas定理
题意
给定
,要求组合数
。
思路
因为
会很大,那么
,也会很大,同时因为存在取模 所以想到Lucas定理。
Lucas定理:
若
是质数,则对于任意整数
,有:
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
const int mod = 1000003;
int cas,n,k;
ll f[N];
//提前求出阶乘
void init(){
f[0] = 1;
f[1] = 1;
for(int i = 2; i < N; i++){
f[i] = f[i - 1] * i % mod;
}
}
//快速幂
ll qpow(ll a, ll b){
ll res = 1;
while(b){
if(1 & b){
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
//求组合数
ll C(int n,int m){
return f[n] * qpow(f[m]*f[n - m] % mod,mod - 2) % mod ;
}
//Lucas定理
ll Lucas(ll n,ll m){
if(!m)
return 1;
return C(n % mod,m % mod) * Lucas(n / mod,m / mod) % mod;
}
int main(){
scanf("%d",&cas);
init();
int icas = 1;
while(cas--){
scanf("%d %d",&n,&k);
printf("Case %d: %lld\n",icas++,Lucas(n,k));
}
return 0;
}
H - How Many Points?——扩展欧几里得
题意
在二维网格中,给定两个点 的坐标,问线段 上有多少个网格点?
思路
设
,点
在线段
上,那么必然有
可得
令
到此我们的扩展欧几里得的模型就出来了。
我们要算有多少组满足在 之间的解,分别求出满足条件的 的个数,取两者的最小值即可。
以求
的解的个数为例,
同理:
的通解为
那么在 之间的 值就是
那么,满足条件的 的个数就是 ,加的 是 点。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
ll cas,a1,b1,a2,b2;
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 x0 = x,y0 = y;
x = y0;
y = x0 - a / b * y0;
return t;
}
int main(){
scanf("%lld",&cas);
int icas = 1;
while(cas--){
scanf("%lld %lld %lld %lld",&a1,&b1,&a2,&b2);
ll A = b1 - b2, B = a2 - a1,C = a2*b1 - a1*b2,x,y,x0;
ll g = exgcd(A,B,x,y);
if(A == 0){
printf("Case %d: %lld\n",icas++,abs(B)+1);
continue;
}
if(B == 0){
printf("Case %d: %lld\n",icas++,abs(A)+1);
continue;
}
if(C % g != 0){
printf("Case %d: 2\n",icas++);
continue;
}
//printf("A = %lld B = %lld C = %lld\n",A,B,C);
A /= g;
B /= g;
C /= g;
//printf("A = %lld B = %lld C = %lld\n",A,B,C);
//x0 = (x * C % B + B) % B
ll ans = min(abs(a2 - a1) / abs(B),abs(b2 - b1) / abs(A));
printf("Case %d: %lld\n",icas++,ans + 1);
}
return 0;
}
I - Trailing Zeroes (II)——分解因子
题意
求 的后导0的个数。
思路
后缀 的个数就是因子 和 的个数。所以我们的目的就是分解出因子 ,提前预处理出 ~ 的所有数的因子 和 ,保存下来,然后求一个前缀和。
然后求出整个式子因子 和 的个数:
式子中因子 的个数:
ans2[n] - ans2[r] - ans2[n - r] + tow[p]*q;
式子中因子 的个数:
ans[n] - ans[r] - ans[n - r] + five[p]*q;
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
int cas,n,r,p,q;
int ans[N],five[N],tow[N],ans2[N];
void init(){
for(int i = 1; i < N; i++){
int tmp = i;
while(tmp % 2 == 0 ){
tow[i]++;
tmp /= 2;
}
tmp = i;
while(tmp % 5 == 0 ){
five[i]++;
tmp /= 5;
}
}
for(int i = 1; i < N; i++){
ans[i] = ans[i-1] + five[i];
ans2[i] = ans2[i-1] + tow[i];
}
}
ll solve(){
ll res1,res ;
res = ans[n] - ans[r] - ans[n - r] + five[p]*q;
res1 = ans2[n] - ans2[r] - ans2[n - r] + tow[p]*q;
return min(res,res1);
}
int main(){
scanf("%d",&cas);
init();
int icas = 1;
while(cas--){
scanf("%d %d %d %d",&n,&r,&p,&q);
printf("Case %d: %lld\n",icas++,solve());
}
return 0;
}
J - A New Function ——因子和
题意
表示
的的因子和 ,现要求出
。
思路
一个小知识点:
中因子中含有
的数字个数为
。
我们来简单证明一下:
那么我们的问题就转化为
直接数论整除分块解决
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll unsigned long long
using namespace std;
int cas,n;
ll solve(){
ll res = 0;
for(ll l = 2,r; l <= n; l = r + 1){
r = n / (n / l);
res += (r * (r + 1) / 2 - l *(l-1) / 2) * (n / l - 1);
}
return res;
}
int main(){
scanf("%d",&cas);
int icas = 1;
while(cas--){
scanf("%d",&n);
printf("Case %d: %llu\n",icas++,solve());
}
return 0;
}
K - False Ordering
题意
按照以下规则重新排序:
- 因子个数多数的放前面。
- 因子个数相同时,数字大的放前面。
思路
数据规模较小,直接记录每个数字的因子个数,保存在结构体中,重写排序函数,直接sort();
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 1001;
struct Div_cnt{
int num , sum;
}div_cnt[N];
int prime[N],prime_tot = 0;
bool prime_tag[N] = {0};
int cas,icas = 1,n,cnt = 1;
bool cmp (const Div_cnt &a,const Div_cnt &b){
if(a.sum == b.sum) return a.num > b.num;
return a.sum < b.sum;
}
void solve(){
for(int i = 1; i < N; i++){
div_cnt[i].num = i;
div_cnt[i].sum = 0;
for(int j = 1 ; j * j <= i && j < N; j++)
if(i % j == 0){
if(j * j == i){
div_cnt[i].sum += 1;
}else
div_cnt[i].sum += 2;
}
}
sort(div_cnt + 1,div_cnt + N,cmp);
}
int main(){
solve();
scanf("%d",&cas);
while(cas--){
scanf("%d",&n);
printf("Case %d: %d\n",icas++,div_cnt[n].num);
}
return 0;
}
L - Trailing Zeroes (III)——约数 和 二分答案
题意
给定一个
,求一个数
,需要满足
有
个后缀
,要求
尽量小。
思路
此题已经在[kuangbin] 专题十四 写过了 使用约数性质和二分答案解题。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#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;
else
return 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;
}
M - Bank Robbery ——解方程
题意
给出
,并且已知
,要求求出符合条件的
,多个按字典序排列。
思路
已知
设
可知 的范围为 ~
结合
两式可得
是给定的所以我们只需要从
~
枚举
判断
是否是
的倍数即可,倒着枚举是为了输出顺序。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll unsigned long long
using namespace std;
ll cas,n,icas = 1;
int main(){
scanf("%llu",&cas);
while(cas--){
scanf("%llu",&n);
printf("Case %llu:",icas++);
for(int i = 9 ; i >= 0; i--){
if((n - i) % 9 == 0)
printf(" %llu",(n - i) / 9 * 10 + i);
}
}
return 0;
}
N - Help Hanzo——区间筛法
题意
给定区间
,问
中有多少素数。
思路
同是[kuangbin] 专题十四 的题目。
使用区间筛法
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
int icas = 1;
ll cas,a,b;
ll prime[N],prime_tot = 0;
bool prime_tag[N],bk[N];
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(){
for(int i = 0; i <= b - a; i++) bk[i] = true;
for(int i = 0 ; i < prime_tot; i++){
ll tmp = a / prime[i] * prime[i];
if(tmp < a ) tmp += prime[i];
if(tmp < prime[i]*prime[i]) tmp = prime[i]*prime[i];
while(tmp <= b){
//printf("tmp = %d\n",tmp);
bk[tmp - a] = false;
tmp += prime[i];
}
}
ll res = 0;
for(int i = 0 ; i <= b - a; i++)
if(bk[i]) res++;
if(a == 1)
res--;
return res;
}
int main(){
get_prime();
scanf("%lld",&cas);
while(cas--){
scanf("%lld %lld",&a,&b);
printf("Case %d: %lld\n",icas++,solve());
}
return 0;
}
O - 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;
}
思路
同是[kuangbin] 专题十四 的题目。
#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;
}
P - Large Division——模拟大整数取余
题意
给两个数 , 很大, 在整数范围。判断 能不能整除 。
思路
模拟 整除 的过程。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
string a;
ll b,cas ,icas = 1;
int main() {
ios::sync_with_stdio(false);
cin>>cas;
while(cas--) {
cin>>a>>b;
b = abs(b);
ll sum = 0,len = a.length();
int i;
if(a[0] == '-') i = 1;
else i = 0;
for(; i < len; i++) {
sum = sum * 10 + a[i] - '0';
sum = sum % b;
}
if(sum == 0){
cout<<"Case "<<icas++<<": divisible\n";
}else{
cout<<"Case "<<icas++<<": not divisible\n";
}
}
return 0;
}
Q - Finding LCM——唯一分解定理
题意
已知 ,给定 ,求满足条件的最小的 。
思路
利用唯一分解定理和最小公倍数的一个性质:
令:
则
将
分别分解质因数为
和
,枚举每一项质因子,如果该项上质因子的次方不是最大值,我们就乘上一个该质因子的最大次幂,进行累乘,这样最终累乘的结果就是答案。
例如:
可分解成
初始
,枚举到质因子
的时候,累乘上一个
最后得
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 10;
ll a,b,cas,icas = 1,l;
int c[N] = {0}, ans[N] = {0};
int prime[N],prime_tot = 0,p[N],cnt = 0;
bool prime_tag[N] = {0};
ll gcd(ll a, ll b){
return b == 0 ? a : gcd(b,a%b);
}
ll lcm(ll a, ll b){
return a * b / gcd(a,b);
}
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 qpow(ll a,ll b) {
ll res = 1;
while(b) {
if(b & 1) res = res * a;
a = a * a;
b /= 2;
}
return res;
}
void divide1(ll n) {
for(int i = 0 ; i < prime_tot; i++) {
if(n % prime[i] == 0) {
c[prime[i]]= 0;
while(n % prime[i] == 0 && n > 1) {
c[prime[i]]++;
n /= prime[i];
//intf("yes ");
}
}
if(n == 1) break ;
}
}
void divide2(ll n) {
for(int i = 0 ; i < prime_tot; i++) {
if(n % prime[i] == 0) {
p[++cnt] = prime[i];
ans[prime[i]]= 0;
while(n % prime[i] == 0 && n > 1) {
ans[prime[i]]++;
n /= prime[i];
//intf("yes ");
}
}
if(n == 1) break ;
}
}
ll solve(ll n) {
divide1(n);
ll res = 1;
for(int i = 1 ; i <= cnt; i++) {
if(ans[p[i]] > c[p[i]]){
res *= qpow(p[i],ans[p[i]]);
}
}
return res ;
}
int main() {
get_prime();
scanf("%lld",&cas);
while(cas--) {
ll ans1,ans2;
scanf("%lld %lld %lld",&a,&b,&l);
cnt = 0;
printf("Case %d: ",icas++);
memset(ans,0,sizeof(ans));
memset(p,0,sizeof(p));
memset(c,0,sizeof(c));
if(l % a != 0 || l % b != 0) {
printf("impossible\n");
continue;
}
divide2(l);
ans1 = solve(lcm(a,b));
printf("%lld\n",ans1);
}
return 0;
}
/*
1
24702 3202 1661011884
*/
R - Mysterious Bacteria——算术基本定理
题意
给一个
,把
表示为
的形式,问最大的
的值是多少。
思路
算术基本定理分解 ,然后找出一个最大值。
但是,这一题有个坑,
可能是负数,这就要求
在
为复数的时候要是一个奇数。
所以如果一开始分解出来的
是一个偶数,要一直除以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;
}
S - 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]);
else
printf("Case %d: %.10f\n",c,log(n) + r + 1.0/(2*n));
}
return 0;
}
T - Pairs Forming LCM —— 唯一分解定理(算术分解定理)
同是[kuangbin] 专题十四 的题目。
题意
求出小于
的数中,有多少数对的最小公倍数是
。
思路
由算术分解定理可以求出最大公约数和最小公倍数:
对于两个整数
将
分解:
将 分解:
GCD( )=
LCM( )=
再将GCD( )分解:GCD( ) 。
可知:
如果 ,那么 就可以取 共 种情况
同时,对称的来说,如果 ,那么 就可以取 共 种情况。
同时因为 的情况被算了两次,所以要剪掉一次,这样一来第i项就一共有 种情况了。
将每一项的情况相乘,就可以得出所有可能的情况,同时因为我们计算的时候是对称的,所以我们计算出的结果是真正结果的两倍。举个栗子:当 的时候, 和 其实是一种情况,同时因为 的情况我们只算了一次,所以要加上一次 的情况,保证所有情况都出现了两次,再统一除上 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;
}
U - Harmonic Number (II) —— 数论整除分块
同是[kuangbin] 专题十四 的题目。
题意
给出
,求和:
。
思路
原本是抄网上的思路,后来仔细一想 这不是就是整除分块吗?直接一首模板
#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;
}
V - Goldbach`s Conjecture ——素数
同是[kuangbin] 专题十四 的题目。
题意:
给出几组测试数据,每组给出一个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;
}
W - Sum of Consecutive Integers —— 求奇因子个数
题意
给定一个数 ,可以被分解成多个连续数的和,问有多少中分解的方法?
例如, 有 3 个解,
思路
我们先设 分解成连续的 个数,第一项为 ,根据等差数列的求和公式可以知道
我们整理一下:
这样一来我们可以得出一个结论:
-
一定是一个奇数,并且是
的一个因子。
所以我们的问题就转化成了一个:求 有多少个奇因子的个数。
根据唯一分解定理的推论:
的正约数个数为(
表示连乘)
因为要求是奇因子,我们只需要不乘第一个因子 ,就是奇因子。所以我们去掉 中的 ,和出现 的情况,即所有的 都取 的情况,剩下的就是答案了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 1e7 + 5;
int cas,icas = 1;
ll n;
ll prime[666666];
int prime_tot = 0;
bool prime_tag[N] = {0};
void get_prime(){
for(ll i = 2ll; i < N; i++){
if(!prime_tag[i]){
prime[prime_tot++] = i;
}
for(ll 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(ll i = 0; i < prime_tot && prime[i] * prime[i] <= n; i++){
ll c = 0;
while(n % prime[i] == 0 && n > 1){
c++;
n /= prime[i];
}
if(i != 0)
res *= c + 1;
if(n == 1) break;
}
if(n > 2)
res *= 2;
res--;
return res;
}
int main() {
get_prime();
scanf("%d",&cas);
while(cas--) {
scanf("%lld",&n);
printf("Case %d: %lld\n",icas++,solve(n));
}
return 0;
}