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)cur∗c=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+2∗n1
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+k−1
那么显然有
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=−(ai−1)+...+−1+0+1+...+(ai−1)+ai+ai+1+...+ai+k−1
那么由于正负对称,再加上中间的0,现在分成了
k
′
k'
k′份,
k
′
=
k
+
(
a
i
−
1
)
∗
2
+
1
k' = k + (a_i - 1) * 2 + 1
k′=k+(ai−1)∗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∗(k−1)/2≥2∗ai+k−1
同时
k
′
=
k
+
2
∗
a
i
−
2
k' = k + 2 * a_i - 2
k′=k+2∗ai−2,所以
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;
}