2020牛客暑期多校训练营(第七场)(2020.8.1)
B、Mask allocation
可以看成把一个 m × n m×n m×n的长方形划成若干个宽为 1 1 1的矩形,使得存在两种分割方法能恰好填满长和宽。然后只要不断在矩形里划分正方形就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
int n, m; scanf("%d%d", &n, &m);
vector <int> ans;
while (m != n)
{
if (m > n) swap(m, n);
for (int i = 1; i <= m; ++i) ans.push_back(m);
n -= m;
}
for (int i = 1; i <= n; ++i) ans.push_back(m);
cout << ans.size() << endl;
for (auto &i : ans) printf("%d ", i);
cout << endl;
}
return 0;
}
D、Fake news
题面生草。
打个小表,比如到1e4,发现只有1和24。之后肯定不可能再有其它数了。
平方求和公式按立方速度增长,最后肯定不可能夹在两个平方数之间的。
代码由璇大师提供。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
long long n;
scanf("%lld", &n);
if (n == 1 || n == 24) printf("Fake news!\n");
else
{
printf("Nobody knows it better than me!\n");
}
}
}
H、Dividing
这题做得太痛苦了。思路看完题大概七八分钟不到就有了,但是一直写不出来。最后磨了两个小时才过。
第一种操作每次加上 k k k,所以会发现二元组左边的数模 k k k一定余 n n n。而一旦执行了一次第二种操作,之后无论再进行第一种或第二种操作,模 k k k一定都是0。初始状态 n n n都是1,所以余数只可能是 0 0 0或 1 1 1。而且可以证明对于小于 N N N的所有数,只要它模 k k k同余0或1就一定能构造出这样一对符合题意的二元组。所以问题转化成对于所有小于 K K K的数 k k k,求模 k k k同余0或1的总数。
之后考虑枚举 k k k。对于某一个 k k k而言,1~ N N N中是 k k k倍数的数有 ⌊ N k ⌋ \lfloor {N\over k} \rfloor ⌊kN⌋个。同余1的数可以这么计算: 1 + p k ≤ N 1+pk\le N 1+pk≤N,解出 p ≤ ⌊ N − 1 k ⌋ p\le {\lfloor {{N-1}\over k}\rfloor} p≤⌊kN−1⌋,其中 p p p为整数。但是这个式子中 p p p可以取到 0 0 0,于是1~ N N N中模 k k k余1的数为 ⌊ N − 1 k ⌋ + 1 {\lfloor {{N-1}\over k}\rfloor}+1 ⌊kN−1⌋+1个。不过对于 k = 1 k=1 k=1要特判,因为此时没有模 k k k同余1的数。
所以最后答案为 Σ k = 2 K ⌊ N k ⌋ + Σ k = 2 K ⌊ N − 1 k ⌋ + ( K − 2 + 1 + N ) = Σ k = 2 K ⌊ N k ⌋ + Σ k = 2 K ⌊ N − 1 k ⌋ + ( K + N − 1 ) \Sigma_{k=2}^{K}\lfloor {N\over k} \rfloor+\Sigma_{k=2}^{K}{\lfloor {{N-1}\over k}\rfloor}+(K-2+1+N)=\Sigma_{k=2}^{K}\lfloor {N\over k} \rfloor+\Sigma_{k=2}^{K}{\lfloor {{N-1}\over k}\rfloor}+(K+N-1) Σk=2K⌊kN⌋+Σk=2K⌊kN−1⌋+(K−2+1+N)=Σk=2K⌊kN⌋+Σk=2K⌊kN−1⌋+(K+N−1)
之后就不需要管题面了,只要把这坨东西求出来就好。
然后发现枚举 k k k就炸了。然后开始脱发。
苦恼将近一个半小时,发现可以分块。因为对于某一段区间内的 k k k而言,下取整可能是一样的。之后从2开始枚举 k k k,每次计算出当前值对应的右边界,乘起来之后再把 k k k赋成右边界加一就好了。复杂度似乎是 O ( N ) O(\sqrt N) O(N)的。具体我也不知道,也不会证。反正能过。
代码里还有个坑点就是右边界要在 r r r和 K K K当中取个min。不然有可能直接就跳出去了。第一次就这里哇了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
int main()
{
ll n, k, sum; cin >> n >> k; sum = n;
for (ll i = 2; i <= k; )
{
ll temp = n / i;
if (temp == 0) break;
ll l = i, r = n / temp;
sum = (sum + ((min(r, k) - l + 1)) % MOD * (temp % MOD)) % MOD;
i = r + 1;
}
for (ll i = 2; i <= k; )
{
ll temp = (n - 1) / i;
if (temp == 0) break;
ll l = i, r = (n - 1) / temp;
sum = (sum + ((min(r, k) - l + 1)) % MOD * (temp % MOD)) % MOD;
i = r + 1;
}
sum = (sum + k - 1) % MOD;
cout << sum << endl;
return 0;
}
赛后总结:
H卡了很久。做完H之后D很快就出了。应该先去D再返回看H的。
之后开始罚坐。
另外就是英语太差,J看不懂。吐了。
什么,你问我前面几场的总结和题解呢?
在补了在补了.jpg