Codeforces Round 1057 (Div. 2)

链接:https://codeforces.com/contest/2153

A. Circle of Apple Trees

算法:

    贪心。

思路:

    不同元素的个数。

关键代码:

void miyan()
{
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto &x : a)
        cin >> x;

    cout << set(all(a)).size() << endl;
}

B. Bitwise Reversion

算法:

    位运算。

思路:

    无。

关键代码:

void miyan()
{
    ll x, y, z;
    cin >> x >> y >> z;

    ll a = x | z, b = x | y, c = y | z;

    if ((a & b) != x || (b & c) != y || (a & c) != z)
        cout << "NO" << endl;
    else
        cout << "YES" << endl;
}

C. Symmetrical Polygons

算法:

    计算几何,贪心,构造,排序。

思路:

    由三角形两边之和大于第三边可推广至多边形:最大的边小于前 i – 1 条边的和。

    要求对称,每次选两条相等的边。

    对于出现奇数次数的边,单独存储。

    最后可以选一条或者两条边。

    对于一条边选可选择最大的即可。

    对于两条边选尽可能大的边和次大边。

关键代码:

void miyan()
{
    int n;
    cin >> n;
    vector<ll> a(n);
    for (int i = 0; i < n; ++i)
        cin >> a[i];

    map<ll, ll> m;
    for (auto x : a)
        ++m[x];

    ll sum = 0;
    ll tot = 0;
    vector<int> have;
    for (auto [val, cnt] : m)
    {
        ll t = cnt;
        if (cnt & 1)
        {
            have.push_back(val);
            --t;
        }

        sum += val * t;
        tot += t;
    }

    ranges::sort(have);

    bool ok1 = 0, ok2 = 0;
    ll ans = sum;
    ll len = have.size();
    for (int i = 0; i < len; ++i)
    {
        int a = have[i], b;
        if (a < sum)
        {
            ans = max(ans, sum + a);
            ok1 = 1;
        }

        if (i < len - 1)
        {
            b = have[i + 1];
            if (a + sum > b)
            {
                ans = max(ans, a + b + sum);
                ok2 = 1;
            }
        }
    }

    if ((ok2 && tot) || (!ok2 && ok1 && tot > 1) || (!ok1 && !ok2 && tot > 2))
        cout << ans << endl;
    else
        cout << 0 << endl;
}

D. Not Alone

算法:

    \(dp\),贪心。

思路:

    结论:将数组分成长度为 \(2\) 或者 \(3\) 的段是最优的。

    长度为 \(2\) 的段代价为 \(abs(a_i – a_{i + 1})\),长度为 \(3\) 的段代价为 \(max(a_{i – 1}, a_i, a_{i + 1}) – min(a_{i – 1}, a_i, a_{i + 1})\)。

    为什么长度为 \(2\) 的段或者长度为 \(3\) 的段是最优的。

    证明长度为 \(2\) 的段是最优的:

    对于一个长度为 \(m > 3\) 的相同块,记为 \(a_1, a_2, …………, a_m\),要将全部元素都变为 \(x\),所需的代价为 \(Σabs(a_i – x)\),拿出最后两个使之成为单独块,记为 \(C\),那么此时代价为 \(\sum_{i=1}^{m – 2} abs(a_i – x) + abs(a_m – a_{m – 1})\),此时需要考虑 \(C\) 先的代价变化,既考虑 \(abs(a_{m – 1} – x) + abs(a_m – x)\) 和 \(abs(a_m – a_{m – 1})\) 的大小,不难发现只有 \(a_{m – 1}\) 或者 \(a_m\) 其中一个小于等于 \(x\) 或者 大于等于 \(x\),而另一个要相反,既 \(a_{m – 1}\) 和 \(a_m\) 在 \(x\) 的两边。

    其他情况都是大于的。那么可知分成大小为两的块代价只会不变或者减少。

    长度为 \(3\) 的情况同理。

    通过以上证明,问题就转换为了将数组中的连续元素可以分成长度为 \(2\) 或者 \(3\) 的块,每个块需要变成一样的值,求最小代价。

    考虑 \(dp\) 解。

    如果数组不是环形的,\(dp[i] = min(dp[i], dp[i + 2] + abs(a_i – a_{i + 1}), dp[i + 3] + max(a_{i – 1}, a_i, a_{i + 1}) – min(a_{i – 1}, a_i, a_{i + 1}))\);

    考虑数组是环形,只需考虑 \(a_n, a_1\) 或者 \(a_{n – 1}, a_n, a_1\) 或者 \(a_n, a_1, a_2\) 在同一个块,对数组做一个循环移位即可。

关键代码:

const ll inf = 1e18;

void miyan()
{
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (auto &x : a | views::drop(1))
        cin >> x;

    ll ans = inf;
    for (int _ = 0; _ < 4; ++_)
    {
        vector<ll> dp(n + 1, inf);
        dp[0] = 0;
        for (int i = 2; i <= n; ++i)
        {
            dp[i] = min(dp[i], dp[i - 2] + abs(a[i] - a[i - 1]));
            if (i > 2)
                dp[i] = min(dp[i], dp[i - 3] + max({a[i], a[i - 1], a[i - 2]}) - min({a[i], a[i - 1], a[i - 2]}));
        }

        for (int i = 1; i < n; ++i)
            swap(a[i], a[i + 1]);

        ans = min(ans, dp[n]);
    }

    cout << ans << endl;
}

E. Zero Trailing Factorial

    还没补。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇