校队训练 | 数论专题

2021.2-7-2021.2-9 校队训练纪念戳
周队 为啥题目这么多啊qwq 数理逻辑作业还没动呢
咕咕咕 慢速补题中

A-找新朋友

不多说了,就是普通的求欧拉函数模板qwq
欧拉函数:在小于等于n中与n互素的数的个数。

#include<bits/stdc++.h>
#define MAXN 100010
using namespace std;
int n, cnt, T;
long long ans;
int p[MAXN];
int main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%d", &T);
	while(T--){
		scanf("%d", &n);
		cnt = 0;
		ans = 1ll * n;
		for(int i = 2; i * i <= n; i++){
			if(n % i == 0){
				ans = ans / i * (i - 1);
				while(n % i == 0) n /= i;
			}
		}
		if(n > 1) ans = ans / n * (n - 1);
		printf("%lld\n", ans);
	}
	return 0;
}

B-GCD Again

#include<bits/stdc++.h>
#define MAXN 100010
using namespace std;
int T;
inline long long calc(int n){
	long long ans = 1ll * n;
	for(int i = 2; i * i <= n; i++){
		if(n % i == 0){
			ans = ans / i * (i - 1);
			while(n % i == 0) n /= i;
		}
	}
	if(n > 1) ans = ans / n * (n - 1);
	return ans;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	int n;
	scanf("%d", &n);
	while(n != 0){
		long long ans;
		if(n <= 3) ans = 0;
		else ans = 1ll * n - 1 - calc(n);
		printf("%lld\n", ans);
		scanf("%d", &n);
	}
	return 0;
}

C-Calculation 2

如果我们能求出来小于n的正整数和n互素的数的和,直接用1到n的总和减去它即可。
小于n的正整数和n互素的数的和 = n * phi(n) / 2

#include<bits/stdc++.h>
#define MAXN 100010
#define mod 1000000007
#define int long long
using namespace std;
int T;
inline int calc(int n){
	int ans = 1ll * n;
	for(int i = 2; i * i <= n; i++){
		if(n % i == 0){
			ans = ans / i * (i - 1);
			while(n % i == 0) n /= i;
		}
	}
	if(n > 1) ans = ans / n * (n - 1);
	return ans;
}
inline int fqow(int x, int y){
	int ans = 1;
	while(y){
		if(y & 1) ans = x * ans % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}
signed main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	int n;
	scanf("%lld", &n);
	while(n != 0){
		int ans = 1ll * (n - 1) * n % mod * fqow(2, mod - 2) % mod;
		int phi = calc(n);
		ans = (ans - 1ll * n * phi % mod * fqow(2, mod - 2) % mod + mod ) % mod;
		if(n == 1) ans = 0;
		printf("%lld\n", ans);
		scanf("%lld", &n);
	}
	return 0;
}

D-GCD

#include<bits/stdc++.h>
#define int long long
using namespace std;
int T, N, M;
inline int calc(int n){
	int ans = 1ll * n;
	for(int i = 2; i * i <= n; i++){
		if(n % i == 0){
			ans = ans / i * (i - 1);
			while(n % i == 0) n /= i;
		}
	}
	if(n > 1) ans = ans / n * (n - 1);
	return ans;
}
signed main(){
	scanf("%lld", &T);
	while(T--){
		int ans = 0;
		scanf("%lld%lld", &N, &M);
		for(int i = 2; i * i <= N; i++){
			int p = i, b, cur_ans;
			if(N % p == 0){
				if(p >= M){
					b = N / p;
					cur_ans = calc(b);
					ans += cur_ans;
				}
				if(p * p == N) continue;
				p = N / p;
				if(p >= M){
					b = N / p;	
					cur_ans = calc(b);
					ans += cur_ans;
				}
			}
		} 
		ans += calc(1);
		printf("%lld\n", ans);
	}
	return 0;
}

E-Bi-shoe and Phi-shoe

F-Huge Mods

G-Powers Et Al.

H-Power Tower

I-GCD - Extreme (II)

J-Iftar Party

K-Trailing Zeroes (I)

L-Efficient Pseudo Code

M-A New Function

如果对于每一个数来讲,我们可以用O(1)的时间计算它的贡献(向上能加几个就是几*数本身的贡献值)
考虑到因为是2e9个数,一个一个询问也会时间爆炸。
之后发现,当数大到一定程度的时候,一些数他们的贡献次数是一样的,那么我们就可以将它们放在一起进行计算(即分块)
我们将前sqrt(n)个数和后面的数分开计算,前面使用O(1)的方法,以数值进行遍历;后面使用分块,以往上能贡献几次的个数进行遍历(可以发现,也是根号的时间复杂度,因为每块我们都可以用等差数列公式O(1)计算)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int T, cnt, n;
signed main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%lld", &T);
	while(T--){
		++cnt;
		bool flag = false;
		int ans = 0;
		scanf("%lld", &n);
		int limit = (int)sqrt(n + 0.5);
		ans = 1;
		for(int i = 2; i <= limit; i++) ans += n / i * i;
		for(int i = 1; i <= limit; i++){
			int from = n / (i + 1) + 1;
			int to = n / i;
			if(from == limit || from == n) from++;
			if(to == limit || to == n) to--;
			if(from <= to){
				int sum = to - from + 1;
				int add = (from + to) * sum / 2;
				ans += add * i;
			}
		}
		ans -= n * (n - 1) / 2;
		if(n <= 2) ans = 0;
		printf("Case %lld: %lld\n", cnt, ans);
	}
	return 0;
} 

N-Bank Robbery

O-Finding LCM

c u r = l c m ( a , b ) cur = lcm(a,b) cur=lcm(a,b), 那么 l c m ( c u r , c ) = l lcm(cur, c) = l lcm(cur,c)=l 就是 c u r ∗ c g c d ( c u r , c ) = l \frac{cur * c}{gcd(cur, c) } = l gcd(cur,c)curc=l
如果 g c d ( c u r , c ) = = 1 gcd(cur, c) == 1 gcd(cur,c)==1的话, c = l / c u r c = l / cur c=l/cur
如果不是,因为cur无法再改变,我们先令 c = l / c u r c = l / cur c=l/cur,然后给分母除以 g c d ( g c d ( c u r , c ) , c ) gcd(gcd(cur, c), c) gcd(gcd(cur,c),c), 给c乘上 g c d ( g c d ( c u r , c ) , c ) gcd(gcd(cur, c), c) gcd(gcd(cur,c),c)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a, b, c, l, T;
map<int, int>p, ex;
bool flag;
inline int gcd(int x, int y){
	if(y > x) swap(x, y);
	if(y == 0) return x;
	else return gcd(y, x % y);
}
inline void clear(){
	c = 1; 
	return;
}
signed main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%lld", &T);
	for(int tt = 1; tt <= T; tt++){
		clear();
		scanf("%lld%lld%lld", &a, &b, &l);
		int gcd_ab = gcd(a, b);
		int cur = a * b / gcd_ab;
		if(cur > l || l % cur != 0) printf("Case %lld: impossible\n", tt);
		else{
			c = l / cur;
			int gcd_ab_c = gcd(cur, c);
			while(gcd_ab_c != 1){
				c *= gcd_ab_c;
				cur /= gcd_ab_c;
				gcd_ab_c = gcd(cur, c);
			}
			printf("Case %lld: %lld\n", tt, c);
		}
	}
	return 0;
}

P-Harmonic Number

调和级数在很大的时候有一个公式:
a n s ( n ) = l n ( n ) + C + 1 2 ∗ n ans(n) = ln(n) + C + \frac{1}{2*n} ans(n)=ln(n)+C+2n1
C = 0.57721566490153286 C = 0.57721566490153286 C=0.57721566490153286

#include<bits/stdc++.h>
#define MAXN 100010
using namespace std;
int T, n;
double C = 0.57721566490153286;
int a[MAXN];
double ans[MAXN];
inline void solve(){
	double cur_ans = 0;
	for(int i = 1; i < MAXN; i++){
		cur_ans += 1.0 / i;
		ans[i] = cur_ans;
	}
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%d", &T);
	solve();
	for(int i = 1; i <= T; i++){
		scanf("%d", &n);
		if(n < MAXN) printf("Case %d: %.10lf\n", i, ans[n]);
		else{
			printf("Case %d: %.10lf\n", i, log(n) + C + 1.0 / (2.0 * n));
		}
	}
	return 0;
}

Q-Pairs Forming LCM

R-Sum of Consecutive Integers

我们可以通过讨论分成几份,从而找中间数的方法解决问题。转化一下题意
现在 n = ∏ i = 1 n p i k i n = \prod_{i = 1}^n p_i^{k_i} n=i=1npiki
答案就是 ∏ i = 1 n ( k i + 1 ) − 1 \prod_{i=1}^n (k_i+1)-1 i=1n(ki+1)1
题目就转化成了求奇因子即可!

Emmm…但是为什么不讨论分成偶数份呢?
假如我们现在将n分成了k份(k为偶数): n = a i + a i + 1 + . . . + a i + k − 1 n = a_i + a_{i+1} + ... + a_{i + k - 1} n=ai+ai+1+...+ai+k1
那么显然有 n = − ( a i − 1 ) + . . . + − 1 + 0 + 1 + . . . + ( a i − 1 ) + a i + a i + 1 + . . . + a i + k − 1 n = -(a_{i} - 1)+ ... + -1 + 0 + 1 + ... + (a_i - 1) + a_i + a_{i+1} + ... + a_{i + k - 1} n=(ai1)+...+1+0+1+...+(ai1)+ai+ai+1+...+ai+k1
那么由于正负对称,再加上中间的0,现在分成了 k ′ k' k份, k ′ = k + ( a i − 1 ) ∗ 2 + 1 k' = k + (a_i - 1) * 2 + 1 k=k+(ai1)2+1, k ′ k' k为奇数!其实就包含在我们讨论之中了!
但是可能还有一个疑惑,就是为什么能保证 k ′ < = n k' <= n k<=n呢?
因为题目中说了保证n至少分成两份,也就是 n = = a i + k ∗ ( k − 1 ) / 2 ≥ 2 ∗ a i + k − 1 n == a_i + k * (k - 1) / 2 \geq 2 * a_i + k - 1 n==ai+k(k1)/22ai+k1
同时 k ′ = k + 2 ∗ a i − 2 k' = k + 2 * a_i - 2 k=k+2ai2,所以 n > k n > k n>k啦~~~

PS.最后,为什么程序里没有讨论n为奇数,分成2份的情况呢?
其实这是因为如果是奇数,一定能分成n份(中间数为1),将所有数写出来,正负抵消之后我们可以发现,剩下的正数其实就是分成2份的情况。

#include<bits/stdc++.h>
#define MAXN 1000010
#define E7 10000000 
#define int long long
using namespace std;
int n, T, ans, prime_cnt, cnt;
int prime[MAXN];
bool not_prime[E7 + 1];
map<int, int>p;
inline void pre_solve(){
	not_prime[1] = 1;
	for(int i = 2; i <= E7; i++){
		if(not_prime[i] == 0){
			prime[++prime_cnt] = i;
		}
		for(int j = 1; j <= prime_cnt && prime[j] * i <= E7; j++){
			not_prime[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
	return;
}
inline void clear(){
	p.clear();
	ans = 0;
	cnt = 0;
	return;
}
signed main(){
	#ifndef ONLINE_JUDGE
	freopen("ce.in", "r", stdin);
	#endif
	scanf("%lld", &T);
	pre_solve();
	for(int tt = 1; tt <= T; tt++){
		scanf("%lld", &n);
		clear();
		int nn = n;
		for(int i = 1; i <= prime_cnt && prime[i] * prime[i] <= n; i++){
			if(n % prime[i] == 0){
				if(prime[i] != 2) cnt++;
				while(n % prime[i] == 0){
					n /= prime[i];
					p[prime[i]]++;
				}
			}
		}
		if(n > 1) p[n]++;
		int cur_ans = 1;
		for(map<int, int>::iterator it = p.begin(); it != p.end(); it++)
			if((it->first) & 1) cur_ans *= ((it->second) + 1);
		if(cur_ans > 1) ans += cur_ans - 1;
		printf("Case %lld: %lld\n", tt, ans);
	}
	return 0;
}

S-Prime Independence

T-LCM Extreme

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值