Loading

AtCoder Beginner Contest 268 F Best Concatenation

Best Concatenation

贪心 - 相邻交换

考虑两个相邻位置的元素,如果进行交换,会对答案造成怎样的影响

假设现有一个排列,我们拿出其中两个相邻项 \(A\)\(B\)

考虑 \(... + A + B + ...\)\(... + B + A + ...\) 的顺序对答案造成了什么影响

假设单独一个 \(A\)\(B\) 的贡献为 \(ans_A\)\(ans_B\)

\(A\)\(B\)\(X\) 的数量为 \(X_A\)\(X_B\)

\(A\)\(B\) 的数字之和为 \(cnt_A\)\(cnt_B\)

显然不管 \(A\)\(B\) 如何排列,对其前后的答案影响是相同的,于是直接消去

  1. \(A + B\)

答案贡献为:\(ans_A + ans_B + cnt_B * X_A\)

  1. \(B + A\)

答案贡献为:\(ans_A + ans_B + cnt_A * X_B\)

因此两者相互消去 \(ans_A + ans_B\) 后,为

\(cnt_B * X_A\)\(cnt_A * X_B\) 之间的比较

我们想用排序来完成这项比较的工作就必须证明其偏序的传递性

可以让左右两边同时除以 \(cnt_B * cnt_A\)

那么就有 \(\frac{X_A}{cnt_A}\)\(\frac{X_B}{cnt_B}\) 之间的比较

因为其值都与本身的属性有关,都是自身的值,因此显然有传递性

由于整除等原因,直接用 \(cnt_B * X_A\)\(cnt_A * X_B\) 这个的大小关系进行比较即可

之前牛客多校也碰到了此类题目,一直觉得是队友瞎搞,这次题目才开始想看看到底是个啥,发现其实推个式子,化简完之后证明传递性都是很简单的

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
ll x[maxn], num[maxn], mp[maxn];
string s[maxn];

inline bool cmp(int a, int b)
{
    return x[a] * num[b] > x[b] * num[a];
}

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> s[i];
        for(char now : s[i])
        {
            if(now == 'X') x[i]++;
            else num[i] += now - '0';
        }
        mp[i] = i;
    }
    sort(mp, mp + n, cmp);
    ll ans = 0, sum = 0;
    for(int i=n-1; i>=0; i--)
    {
        for(int j=s[mp[i]].length() - 1; j>=0; j--)
        {
            if(s[mp[i]][j] == 'X') ans += sum;
            else sum += s[mp[i]][j] - '0';
        }
    }
    cout << ans << endl;
    return 0;
}
posted @ 2022-09-14 13:41  dgsvygd  阅读(59)  评论(2编辑  收藏  举报