Codeforces Round #638 (Div. 2)

Codeforces Round #638 (Div. 2)(2020.5.2)

这场昨天没报名,今天早上爬起来按惯例补了四题。

感觉奇奇怪怪的构造题比较多。尤其是C这种题,我基本碰到就等于结束了。

膜昨天比赛光速出了四题的大哥%%%

A、Phoenix and Balance

这第一题就卡了我一会儿。

会发现前面所有的数加起来都没最后一个数大,然后我们把最后一个数和前 n 2 − 1 {n \over 2} -1 2n1个数放在一起,把剩下的放在一起,模拟一下就好了。

#include 
using namespace std;
typedef long long ll;
ll pow(int b)
{
    ll ans = 1;
    for (int i = 1; i <= b; ++i) ans *= 2;
    return ans;
}
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n; cin >> n;
        ll sum1 = 0, sum2 = 0;
        for (int i = 1; i < n / 2; ++i) sum1 += pow(i);
        sum1 += pow(n);
        for (int i = n / 2; i < n; ++i) sum2 += pow(i);
        cout << sum1 - sum2 << endl;
    }
    return 0;
}

B、Phoenix and Beauty

这题只是看起来吓人。因为题目没要求最短序列,所以我们考虑构造一个就可以了。

由题意很快知道这是个周期为 k k k的数列。然后把所有数字去个重,如果不同的数字个数都大于 k k k了肯定构造不出来。剩下的数字我们把它用1补成长度为 k k k的数列,然后输出 n n n次。这样 n n n次循环中每次都能找到一个对应于原数列当前位置的数。

#include 
using namespace std;
typedef long long ll;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n, k; cin >> n >> k;
        int a[110]; set <int> st;
        for (int i = 1; i <= n; ++i)
        {
            cin >> a[i]; st.insert(a[i]);
        }
        if (k < st.size()) cout << -1 << endl;
        else
        {
            cout << n * k << endl;
            for (int i = 1; i <= n; ++i)
            {
                for (auto &j : st) cout << j << ' ';
                for (int j = st.size() + 1; j <= k; ++j) cout << 1 << ' ';
            }
            cout << endl;
        }
    }
    return 0;
}

C、Phoenix and Distribution

个人觉得这题很难…自己没做出来。

考的其实是个分类讨论,不过怎么讨论就很玄学。

我们先读个字符串进来排个序。

如果 s [ 0 ] ! = s [ k − 1 ] s[0]!=s[k-1] s[0]!=s[k1],会发现这时最小就是 s [ k − 1 ] s[k-1] s[k1]。因为要分成 k k k个串,每个串不为空,那么每个串至少都要往里面塞一个字符。然后一旦前 k k k个元素不等,那塞一个字符所能得到最大串就是 s [ k − 1 ] s[k-1] s[k1]。之后不管我们对剩下的字符怎么操作,新生成的最大的串 x x x一定有 x ≥ s [ k − 1 ] x\ge s[k-1] xs[k1]。那么就可以把之后的所有元素塞到 s [ 0 ] s[0] s[0]里面,这样依旧保证了 s [ 0 ] < s [ k − 1 ] s[0]s[0]<s[k1],而且使最大的最小。

如果前面的 k k k个元素都相等,我们看剩下来的元素。如果剩下来的元素都相等,那么显然平均分是最优的。如果剩下的元素有不等的,这时平均分就不一定最优了。比如 a a a b b c , k = 3 aaabbc,k=3 aaabbc,k=3,平均分是 a b , a b , a c ab,ab,ac ab,ab,ac,而 a b b c abbc abbc这时是最优的。

这时我们考虑剩下的元素里最大的字符 c c c,而如果 c c c加到了一些字符串里,那这些串中的某一个一定会成为最大的。那既然这样,我们干脆使包含字符 c c c的字符串最小,所以我们直接把剩下的元素全部塞到 s [ 0 ] s[0] s[0]后面就行了。可以感性理解一下这时一定是最小的。

不过这个我自己真的想不到啊…

代码很短。

#include 
using namespace std;
typedef long long ll;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n, k; cin >> n >> k;
        string x, ans = ""; cin >> x;
        sort(x.begin(), x.end());
        if (x[0] != x[k - 1]) cout << x[k - 1] << endl;
        else
        {
            cout << x[k - 1];
            if (x[k] == x[n - 1])
                for (int i = 1; i <= ceil((double)(n - k) / k); ++i) cout << x[n - 1];
            else
                for (int i = k; i < n; ++i) cout << x[i];
            cout << endl;
        }
    }
    return 0;
}

D、Phoenix and Science

这题我就先把我的代码贴出来吧。因为写得太丑了。而且构造方法也非常混乱,我都不知道怎么就过了…

#include 
using namespace std;
typedef long long ll;
ll qpow(int a, int b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1) ans *= a;
        a *= a, b >>= 1;
    }
    return ans;
}
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n; cin >> n;
        int ans = 1, sum = 1, cnt = 0;
        while (1)
        {
            cnt++, sum += qpow(2, cnt - 1);
            ans += sum;
            if (ans >= n) break;
        }
        cout << cnt << endl;
        vector <ll> temp; ll prev = 1, sav = cnt + 1;
        for (int i = 1; i <= cnt; ++i)
        {
            temp.push_back(prev);
            temp[i - 1] = min(prev, (n - sav) / (cnt - i + 1));
            prev += temp[i - 1]; sav += (cnt - i + 1) * temp[i - 1];
        }
        for (auto &i : temp) cout << i << ' ';
        cout << endl;
    }
    return 0;
}

这题的标答比较有参考价值,就直接说标答了。

看一下这题的输出是个差分数组。差分数组和原数组是可以互相转化的。于是考虑构造原数组 a [ ] a[] a[]

设第 i i i天的细菌个数是 a [ i ] a[i] a[i],会发现这堆细菌如果分裂的话最多只会变成两倍,并且最少——就是不分裂——也和前一天的细菌个数相同。所以有这样的关系: a [ i ] ≤ a [ i + 1 ] ≤ 2 a [ i ] a[i]\le a[i+1] \le 2a[i] a[i]a[i+1]2a[i]。而一天增加的重量就是细菌的个数。因此如果定义 a [ 0 ] = 1 a[0]=1 a[0]=1,那么到第 x x x天细菌的总重量就是 Σ i = 0 x a [ i ] \Sigma_{i=0}^{x}a[i] Σi=0xa[i]。因为要求天数最少,我们就可以先用最高速分裂,就是 a [ i + 1 ] = 2 a [ i ] a[i+1]=2a[i] a[i+1]=2a[i]。如果刚好有一天总重量和要求的重量相等了,那我们就构造好了这样的一个数组。如果有一天总重量大于要求的重量了,那我们就把最后一次分裂产生的数删掉。此时若设前面的和为 s s s,那我们就在这个数组后面塞个 n − s n-s ns,之后排个序。这样同样也得到了一个数组。

之后只要输出相邻两个元素的差就可以了。

这题的思路是当差分数组比较难构造时,可以尝试构造原数组,最后可以通过前缀和或者作差相互转化。

我就是一直在构造差分数组然后浪费1h…

#include 
using namespace std;
typedef long long ll;
int main()
{
    int t; cin >> t;
    while (t--)
    {
        int n; cin >> n;
        vector <ll> ans; ll sum = 0;
        ans.push_back(1), sum = 1;
        while (sum < n) ans.push_back(1 << ans.size()), sum = (1 << ans.size()) - 1;
        if (sum > n) ans.pop_back(), ans.push_back(n - ((1 << ans.size()) - 1));
        sort(ans.begin(), ans.end());
        cout << ans.size() - 1 << endl;
        for (int i = 1; i < ans.size(); ++i) cout << ans[i] - ans[i - 1] << ' ';
        cout << endl;
    }
    return 0;
}

最后用正解的算法写出来的程序也很短。

你可能感兴趣的:(Codeforces Round #638 (Div. 2))