Submission #675987

#TimeUsernameProblemLanguageResultExecution timeMemory
675987TigerpantsBoarding Passes (BOI22_passes)C++17
100 / 100
424 ms38340 KiB
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <numeric>
#include <bitset>
#include <queue>
#include <iomanip>
#include <cmath>
#include <string>

using namespace std;

typedef long long int ll;
typedef long double ld;
typedef vector<ll> vll;
typedef vector<ld> vld;
typedef vector<bool> vb;
typedef vector<vll> vvll;
typedef vector<vld> vvld;
typedef vector<vvll> vvvll;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef vector<pll> vpll;
typedef vector<pld> vpld;
typedef set<ll> sll;
typedef set<pll> spll;
typedef map<pll, ll> mpll_ll;
typedef map<ll, pll> mll_pll;
typedef bitset<15> bits;
typedef vector<bits> vbits;
typedef string str;
typedef vector<str> vstr;

#define rep(x, a, b) for (int x = a; x < b; x++)
#define rev_rep(x, a, b) for (int x = a; x >= b; x--)
#define itr_rep(type_, x, b) for (type_::iterator x = b.begin(); x != b.end(); x++)
#define mp(a, b) make_pair(a, b)
#define all(a) a.begin(), a.end()
#define sz(a) a.size()
#define resz(a, b) a.resize(b)
#define sort_all(a) sort(all(a))
#define pb(a) push_back(a)
#define fill_sz(a, b) fill_n(a.begin(), sz(a), b)

const ll MAXN = 100000;
const ll MAXG = 15;
const ld INF = MAXN * MAXN;

ll N, G;
string s;
vvll groups;

vvll prefix_sums;
vvvll passes;

struct Comparer {
    bool operator() (const bitset<15> &b1, const bitset<15> &b2) const {
        return (b1.to_ulong() < b2.to_ulong());
    }
};

bits active(0x0);
map<bits, vld, Comparer> dp_diff;
map<bits, ld, Comparer> dp;

ld calc_passes(ll i, ll k) {
    ld answ = 0;
    rep(j, 0, G) {
        if (!active[j]) {continue;}
        answ += passes[i][j][k];
    }
    answ += ((ld) k * (k - 1)) / 4;
    answ += ((ld) (sz(groups[i]) - k) * (sz(groups[i]) - k - 1)) / 4;
    return answ;
}

void dp_diff_DFS(ll depth=0) {
    if (depth != G) {
        dp_diff_DFS(depth + 1);
        active[depth] = true;
        dp_diff_DFS(depth + 1);
        active[depth] = false;
        return;
    }


    resz(dp_diff[active], G);
    rep(i, 0, G) {
        if (active[i]) {continue;}
        // consider the best way to have ppl from group i board given that groups in active have boarded, store this in dp
        // use ternary search...
        ll l = 0;
        ll r = sz(groups[i]);
        while (r - l > 2) {
            ll m1 = l + (r - l) / 3;
            ll m2 = r - (r - l) / 3;
            ld f1 = calc_passes(i, m1);
            ld f2 = calc_passes(i, m2);
            if (f1 > f2) {
                l = m1;
            } else {
                r = m2;
            }
        }
        

        dp_diff[active][i] = calc_passes(i, l);
        rep(c, l + 1, r + 1) {
            dp_diff[active][i] = min<ld>(dp_diff[active][i], calc_passes(i, c));
        }

        //dp_diff[active][i] = min<ld>(calc_passes(i, l), calc_passes(i, r));
    }
}

ld dp_recursion(bits call) {
    if (dp.count(call)) {return dp[call];}
    ld answ = INF;
    rep (i, 0, G) {
        if (!call[i]) {continue;}
        call[i] = false;
        answ = min<ld>(answ, dp_recursion(call) + dp_diff[call][i]);
        call[i] = true;
    }
    dp[call] = answ;
    return dp[call];
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout << fixed << setprecision(15);

    cin >> s;
    N = sz(s);
    G = 0;
    rep(i, 0, N) {
        while (s[i] - 'A' >= G) {
            G++;
            groups.pb(vll());
        }
        groups[s[i] - 'A'].pb(i);
    }

    // calculate prefix sums
    resz(prefix_sums, G);
    rep(i, 0, G) {
        resz(prefix_sums[i], N + 1);
        prefix_sums[i][0] = 0;
        rep(j, 0, N) {
            prefix_sums[i][j + 1] = prefix_sums[i][j] + (s[j] - 'A' == i);
        }
    }
    
    // calculate passes between groups
    resz(passes, G);
    rep(i, 0, G) {
        resz(passes[i], G);
        rep(j, 0, G) {
            resz(passes[i][j], sz(groups[i]) + 1);
            fill_sz(passes[i][j], 0);
            // calculate the number of cumulative passes if first k of group i enter from front, and remaining |g|i]| - k enter from back
            // from front
            ll running_cumulative = 0;
            rep(k, 1, sz(groups[i]) + 1) {
                running_cumulative += prefix_sums[j][groups[i][k - 1]];
                passes[i][j][k] += running_cumulative;
            }
            // from back
            running_cumulative = 0;
            rev_rep(k, sz(groups[i]) - 1, 0) {
                running_cumulative += prefix_sums[j][N] - prefix_sums[j][groups[i][k]];
                passes[i][j][k] += running_cumulative;
            }
            // we have made sure that groups[i][k] is _excluded_ from counting as passed, as this makes the passes[e][e] case correct
        }
    }

    // calc subset dp
    dp_diff_DFS();


    // find optimal route...
    bits call(0x0);
    dp[call] = 0;
    rep(i, 0, G) {if (sz(groups[i])) {call[i] = true;}}
    cout << dp_recursion(call) << endl;

    return 0;
}

// for each pair of groups, need to know the number of passes if first k enter from front, and last |g[i]| - k enter from back

Compilation message (stderr)

passes.cpp: In function 'int main()':
passes.cpp:36:40: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<long long int>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
   36 | #define rep(x, a, b) for (int x = a; x < b; x++)
......
  167 |             rep(k, 1, sz(groups[i]) + 1) {
      |                 ~~~~~~~~~~~~~~~~~~~~~~~ 
passes.cpp:167:13: note: in expansion of macro 'rep'
  167 |             rep(k, 1, sz(groups[i]) + 1) {
      |             ^~~
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...