mayoko’s diary

プロコンとかいろいろ。

SRM 480 div2 hard: SignalIntelligence

解法

最後の数以外は, number[i] < number[i+1] を満たすように並べていくのが最適なので, 最後の数で場合分けして, 他の数は sort してシミュレーションすると答えが得られます。

なぜなのかを考えてみます。
貪欲を考えるときは, p 番目の数 number[p] と p+1 番目の数 number[p+1] について, number[p] < number[p+1] のほうが得(損することはない)ことを示せば良いです。

最後の数以外を考えているので, number[p] を足した後は 2^i - 1 番目からスタート -> 2^j - 1 番目に移動, のような遷移のみを考えれば良いです。

number[p] < number[p+1] とします。
2^i - 1 番目からスタートしたとして, number[p] を足しこんだ後 2^j - 1 に遷移し, number[p+1] を足しこんだ後も同様に 2^j - 1 に遷移したとします。

この場合, 2^j - 1 からの遷移は number[p] を足しこんでも number[p+1] を足しこんでも 2^(j+1) - 1 になります。

これは, どちらも 2^j - 1 に遷移したということから number[p+1] < 2^j, number[p] < 2^j となることからわかります。

一方で, number[p] では 2^j - 1 に遷移して, number[p+1] では 2^k - 1 に遷移する(j < k)場合は, 次に遷移する変化数が 直前の遷移の変化数よりも増加することはないので, number[p] -> number[p+1] と足しこんでいくほうが得です。

bool done[55];
ll pos[63];

class SignalIntelligence {
public:
    long long encrypt(vector <int> numbers) {
        int n = numbers.size();
        memset(done, false, sizeof(done));
        ll ret = 1ll<<62;
        for (int i = 1; i < 63; i++) pos[i] = pos[i-1]*2+1;
        for (int i = 0; i < n; i++) {
            vector<int> v;
            for (int j = 0; j < n; j++) if (i!=j) v.push_back(numbers[j]);
            sort(v.begin(), v.end());
            ll cur = 0;
            for (int j = 0; j < n-1; j++) {
                cur += v[j];
                cur = *upper_bound(pos, pos+63, cur);
            }
            ret = min(ret, cur+numbers[i]);
        }
        return ret;
    }
};