2020牛客暑期多校训练营(第七场)

2020牛客暑期多校训练营(第七场)(2020.8.1)

B、Mask allocation

可以看成把一个 m × n m×n m×n的长方形划成若干个宽为 1 1 1的矩形,使得存在两种分割方法能恰好填满长和宽。然后只要不断在矩形里划分正方形就行了。

#include 
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 
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+pkN,解出 p ≤ ⌊ N − 1 k ⌋ p\le {\lfloor {{N-1}\over k}\rfloor} pkN1,其中 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 kN1+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=2KkN+Σk=2KkN1+(K2+1+N)=Σk=2KkN+Σk=2KkN1+(K+N1)

之后就不需要管题面了,只要把这坨东西求出来就好。

然后发现枚举 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 
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

你可能感兴趣的:(2020暑期多校)