제출 #734908

#제출 시각아이디문제언어결과실행 시간메모리
734908adrilenBoarding Passes (BOI22_passes)C++17
100 / 100
525 ms354852 KiB
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
typedef pair<int, int> pii;
constexpr int maxn = 1e5 + 5, maxg = 15;

ll dp[1 << maxg] = { 0 };

ll f[maxn] = { 0 };

ll left_pref[maxn][maxg][maxg] = { 0 }, right_pref[maxn][maxg][maxg] = { 0 };
vector <int> groups[maxg];

int n, g;
// Finding the best way to split the new group given that some groups allready are seated
ll query(int allready, int next_one)
{
    int lower = 0, upper = groups[next_one].size(), mid;
    ll sleft = 0, sright = 0;
    while (lower < upper)
    {
        mid = (lower + upper) >> 1;

        // Testing mid
        sleft = sright = 0;      

        sleft += f[mid] + f[groups[next_one].size() - mid];
        sright += f[mid + 1] + f[groups[next_one].size() - mid - 1];

        for (int y = 0; y < g; y++)
        {
            if ((allready & (1 << y)) == 0) continue;


            if (groups[next_one][mid] != 0) 
                sleft += left_pref[groups[next_one][mid] - 1][next_one][y];
            sleft += right_pref[groups[next_one][mid]][next_one][y];

            if (groups[next_one][mid] + 1 != n) 
                sright += right_pref[groups[next_one][mid] + 1][next_one][y];
            sright += left_pref[groups[next_one][mid]][next_one][y];
        }

        // cout << mid << " " << sleft << " \t" << mid + 1 << " " << sright << "\n";

        if (sleft < sright) upper = mid;
        else lower = mid + 1;
    }
    // cout << allready << " " << next_one << " " << "A" << min(sleft, sright) << "\n";
    return min(sleft, sright);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    for (int i = 1; i < maxn - 1; i++) f[i + 1] = f[i] + i; // Double the actual amount


    string s;
    cin >> s;
    n = s.size();
    vector <int> v(n);
    for (int i = 0; i < n; i++) {
        v[i] = s[i] - 'A';
        groups[s[i] - 'A'].emplace_back(i);
        g = max(g, s[i] - 'A' + 1);
    }


    ll sum, seen;
    // Making prefixsum from left
    for (int x = 0; x < g; x++)         // Want to place
    {
        for (int y = 0; y < g; y++)     // Already placed
        {
            if (x == y) continue;
            sum = 0, seen = 0;
            for (int i = 0; i < n; i++)
            {
                if (v[i] == x) sum += seen;
                else if (v[i] == y) seen += 2; // Double the actual amount to keep to integers

                left_pref[i][x][y] = sum; 
                // cout << i << " " << seen << " " << sum << "\n";
            }
        }
    }

    // Making right prefix
    for (int x = 0; x < g; x++)         // Want to place
    {
        for (int y = 0; y < g; y++)     // Already placed
        {
            if (x == y) continue;
            sum = 0, seen = 0;
            for (int i = n - 1; i >= 0; i--)
            {
                if (v[i] == x) sum += seen;
                else if (v[i] == y) seen += 2; // Double the actual amount to keep to integers

                right_pref[i][x][y] = sum;
            }
        }
    }



    // Solving with bitset dp
    ll best;
    for (int i = 0; i < (1 << g); i++)
    {

        best = 2e15;
        for (int y = 0; y < g; y++)
        {
            if (i & (1 << y))
            {
                best = min(best, dp[i - (1 << y)] + query(i - (1 << y), y));
                // Update best
            }
        }
        if (best == 2e15) best = 0;
        dp[i] = best;
    }
    cout << dp[(1 << g) - 1] / 2;
    if (dp[(1 << g) - 1] & 1) cout << ".5";
    cout << "\n";

}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...