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\) 如何排列,对其前后的答案影响是相同的,于是直接消去
- \(A + B\)
答案贡献为:\(ans_A + ans_B + cnt_B * X_A\)
- \(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;
}