2020牛客暑期多校训练营(第七场)
D. Fake News
题目大意
问你 ∑ i = 1 n i 2 \sum_{i=1}^{n}i^{2} ∑i=1ni2是不是平方数
解题思路
打表发现只有当 n = 1 n=1 n=1和 n = 24 n=24 n=24是是平方数
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--) {
ll n;
cin >> n;
if(n == 1 || n == 24) cout << "Fake news!\n";
else cout << "Nobody knows it better than me!\n";
}
return 0;
}
B. Mask Allocation
题目大意
有 n × m n\times m n×m个物品,让你用 k k k个箱子把它们装起来,并且 k k k个箱子可以合并成 n n n个箱子装 m m m个物品,也可以合并成 m m m个箱子装 n n n个物品,让你使得 k k k最小,并且在满足 k k k的条件下满足字典序最大
解题思路
队友提供了一组样例
n
=
16
n=16
n=16,
m
=
6
m=6
m=6的情况,我们使用如下规则:
箱子数 | 物品数 |
12 | 6 |
4 | 4 |
4 | 2 |
手动模拟一下,发现这个答案是这么来的(以下
a
a
a代表箱子数,
b
b
b代表物品数):
首先我们可以使得
a
=
n
=
16
a=n=16
a=n=16,
b
=
m
=
6
b=m=6
b=m=6,那么此时显然可以满足
16
16
16个
6
6
6的情况,我们需要拆/装箱使其满足
6
6
6个
16
16
16
此时我们先拿出
6
6
6个
6
6
6,我们还需要
6
6
6个
10
10
10,还剩下
10
10
10个
6
6
6
我们再拿出
6
6
6个
6
6
6,我们还需要
6
6
6个
4
4
4,还剩下
4
4
4个
6
6
6
再拿出
4
4
4个
4
4
4,我们还需要
2
2
2个
4
4
4,还剩下
4
4
4个
2
2
2
再拿出
2
2
2个
2
2
2,我们还需要
2
2
2个
2
2
2,还剩下
2
2
2个
2
2
2
最后拿出
2
2
2个
2
2
2
合并一下物品数相同的箱子,那么最终的答案就是 12 12 12个 6 6 6, 4 4 4个 4 4 4和 4 4 4个 2 2 2
假设 a > b a>b a>b,也就是说我们不停的用 a a a减去 b b b,然后分出 b b b个 b b b,由于数据小于 1 e 4 1e^{4} 1e4,所以最多减 1 e 4 1e^{4} 1e4次, T < 100 T<100 T<100,复杂度大概为 O ( 1 e 6 ) O(1e^{6}) O(1e6)
AC代码
#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
if (n < m) swap(n, m);
int a = n, b = m;
int res = 0;
v.clear();
while (true) {
if (a == 0) break;
if (a < b) swap(a, b);
v.push_back(b);
a -= b;
res += b;
}
cout << res << '\n';
sort(v.begin(), v.end(), greater<int>());
int len = v.size();
for (int i = 0; i < len; ++i) {
int k = v[i];
for (int j = 0; j < k; ++j) {
cout << k << " ";
}
}
cout << '\n';
}
return 0;
}
H. Dividing
题目大意
对于元组 ( n , k ) (n, k) (n,k),有如下定义:
- ∀ k \forall k ∀k, ( 1 , k ) (1, k) (1,k)为传奇元组
- 若元组 ( n , k ) (n, k) (n,k)为传奇元组,那么 ( n + k , k ) (n + k, k) (n+k,k)也为传奇元组
- 若元组 ( n , k ) (n, k) (n,k)为传奇元组,那么 ( n × k , k ) (n\times k, k) (n×k,k)也为传奇元组
现在给定 N N N和 K K K,问你 ∀ n ∈ [ 1 , N ] \forall n \in [1, N] ∀n∈[1,N], ∀ k ∈ [ 1 , K ] \forall k \in [1, K] ∀k∈[1,K],传奇元组 ( n , k ) (n, k) (n,k)的个数是多少
解题思路
首先从 ( 1 , k ) (1, k) (1,k)手动模拟,发现每次只有两次操作,将 n n n乘以 k k k或者将 n n n加上 k k k,那么任意一个传奇元组 ( n , k ) (n, k) (n,k),如果 n n n不是 k k k的倍数,那么我们可以将 n n n乘上一个 k k k使其变为 k k k的倍数,也就是说任意 k ∣ n k|n k∣n, ( n , k ) (n, k) (n,k)为传奇元组,若你每次都加上一个 k k k,那么就会变成 ( t × k + 1 , k ) (t \times k+1,k) (t×k+1,k),此时 n n n为 k k k的倍数加一
也就是说当且仅当 ( n = 1 ) (n=1) (n=1)或 ( k ∣ n ) (k|n) (k∣n)或 ( k ∣ ( n − 1 ) ) (k|(n-1)) (k∣(n−1))时 ( n , k ) (n, k) (n,k)为传奇元组
那么对于给定的 n n n和 k k k,其个数为 ∑ i = 2 K [ N i ] + [ N − 1 i ] + N + K − 1 \sum_{i=2}^{K}[\frac{N}{i}]+[\frac{N-1}{i}]+N+K-1 ∑i=2K[iN]+[iN−1]+N+K−1(加 N + K − 1 N+K-1 N+K−1是因为 n = 1 n=1 n=1和 k = 1 k=1 k=1的情况需要单独统计,然后减去重复的 ( 1 , 1 ) (1, 1) (1,1))
看一眼数据范围 1 ≤ N , K ≤ 1 0 12 1 \le N, K \le 10^{12} 1≤N,K≤1012,枚举 k k k显然会炸裂,继续观察上式,我们发现在某些块 i ∈ [ l , r ] i \in [l, r] i∈[l,r]之间, [ n i ] = m [\frac{n}{i}]=m [in]=m是固定的,其块区间求法如下:
假设有
[
n
k
1
]
=
m
[\frac{n}{k_{1}}]=m
[k1n]=m
记
n
m
=
k
2
\frac{n}{m}=k_{2}
mn=k2
即
n
=
m
×
k
2
n=m \times k_{2}
n=m×k2
那么区间
∀
k
∈
[
k
1
,
k
2
]
\forall k \in [k_{1},k_{2}]
∀k∈[k1,k2],
[
n
k
]
=
m
[\frac{n}{k}]=m
[kn]=m,因为
n
=
m
×
k
2
n=m \times k_{2}
n=m×k2,表明
k
2
k_{2}
k2一定是所求块的右区间,因为若
k
>
k
2
k>k_{2}
k>k2,那么
m
m
m必定会减小
综上所述,我们使用分块思想对求和式进行优化,最终复杂度大概为 O ( k ) O(\sqrt{k}) O(k)
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
ll n, k;
cin >> n >> k;
ll res = (n + k - 1) % mod;
for(ll i = 2; i <= k; ) {
ll tmp = n / i;
if(!tmp) break;
ll l = i, r = min(n / tmp, k);
res = (res + (r - l + 1) * tmp % mod) % mod;
i = r + 1;
}
for(ll i = 2; i <= k; ) {
ll tmp = (n - 1) / i;
if(!tmp) break;
ll l = i, r = min((n - 1) / tmp, k);
res = (res + (r - l + 1) * tmp % mod) % mod;
i = r + 1;
}
cout << res << '\n';
return 0;
}