Cod sursa(job #3336019)

Utilizator iulia_morariuIuli Morariu iulia_morariu Data 23 ianuarie 2026 23:54:46
Problema Ghiozdan Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 4.52 kb
#include <algorithm>
#include <iostream>
#include <fstream>
#include <climits>
#include <vector>
#include <queue>
#include <cmath>
// #include <bits/stdc++.h>
#define in  fin
#define out fout
#define int long long

using namespace std;
using ll = long long;
const int GMAX = 75005, NMAX = 20000;

ifstream fin("ghiozdan.in");
ofstream fout("ghiozdan.out");

int n, g;
int dp[GMAX][2];
int ult[GMAX][2]; // l-as fi numit r da coincide cu ala din divide
int v[NMAX];
int vf[201]; // minimude201 reference

void imparte_si_cucereste(int l, int r, int g_acuma){ // care e modul bun sa fac pe L - R costul g_acuma?
    if(l == r){ // cred ca am lowkey terminat
        // cout << "ajung la l = r = " << l << " si g = " << g_acuma << '\n';
        int cnt = g_acuma / l; // ca gen de atatea ori il pun
        if(cnt > vf[l]) out << "oh no\n"; // nush ce sa fac daca se intampla asta deci osa sper ca nu se intampla
        for(int i = 0; i < cnt; i++) out << l << '\n';
        return;
    } // asta cred ca ii bine

    int m = (l + r) / 2;

    // si osa fac dinamica pana la mijloc -- > bun
    // fac last

    if(g_acuma <= 0) return;

    for(int i = 0; i <= g; i++) dp[i][0] = dp[i][1] = INT_MAX;

    // cout << "Sunt la ( " << l << " , " << r << " ) g = " << g_acuma << '\n';
    dp[0][(l - 1) % 2] = 0;
    for(int nr = l; nr <= r; nr++){
        
        // if(vf[nr] == 0) continue;
        for(int i = 0; i <= g_acuma; i++) dp[i][nr % 2] = INT_MAX;
        dp[0][nr % 2] = 0;

        for(int r = 0; r < nr; r++){ // restul pt care fac
            deque <int> dq; // poz
            for(int j = 0; r + j * nr <= g_acuma; j++){
                // acuma scot gen toate pozitiile care is mai mari ca al meu (ca eu vr minimul)
                while(!dq.empty() && dp[ dq.back() * nr + r ][(nr - 1) % 2] - dq.back() > dp[j * nr + r][(nr - 1) % 2] - j) dq.pop_back(); // get outta here
                // acuma sa scot de la inceput ca e in plus
                dq.push_back(j);
                while(!dq.empty() && dq.front() < j - vf[nr]) dq.pop_front();

                if(!dq.empty()){
                    dp[j * nr + r][nr % 2] = dp[dq.front() * nr + r][(nr - 1) % 2] + (j - dq.front());
                    ult[j * nr + r][nr % 2] = ult[dq.front() * nr + r][(nr - 1) % 2];
                    // cout << "Trag " << j * nr + nr << " din " << dq.front() * nr + r << " adica " << ult[dq.front() * nr + r][(nr - 1) % 2] << '\n';
                }
            }
        }

        if(nr == m){
            // cout << "nr = " << nr << " dp = ";
            // for(int i = 0; i <= g; i++) cout << dp[i][nr % 2] << " ";
            // cout << '\n';

            for(int i = 0; i <= g_acuma; i++) ult[i][nr % 2] = i;
        }
    }

    // cout << "dp : ";
    // for(int i = 0; i <= g; i++) cout << dp[i][0] << " ";
    // cout << '\n';

    int last = g_acuma;
    while(dp[last][r % 2] == INT_MAX) last--;

    // cout << "last = " << last << '\n';
    // cout << "ult = " << ult[last][r % 2] << '\n';

    imparte_si_cucereste(l, m, ult[last][r % 2]);
    imparte_si_cucereste(m + 1, r, last - ult[last][r % 2]);
}

signed main(){
    ios_base::sync_with_stdio(false);
    in.tie(NULL);

    in >> n >> g;
    for(int i = 0; i < n; i++){
        in >> v[i];
        vf[ v[i] ]++;
    }

    // initial trebe sa fac dinamica simpla
    // uhhhhhghhhh chiar n-am chef

    for(int i = 0; i <= g; i++) dp[i][0] = dp[i][1] = INT_MAX;

    dp[0][0] = 0;
    for(int nr = 1; nr <= min(200LL, g); nr++){
        // if(vf[nr] == 0) continue;
        for(int i = 0; i <= g; i++) dp[i][nr % 2] = INT_MAX;
        dp[0][nr % 2] = 0;

        for(int r = 0; r < nr; r++){ // restul pt care fac
            deque <int> dq; // poz
            for(int j = 0; r + j * nr <= g; j++){
                // acuma scot gen toate pozitiile care is mai mari ca al meu (ca eu vr minimul)
                while(!dq.empty() && dp[ dq.back() * nr + r ][(nr - 1) % 2] - dq.back() > dp[j * nr + r][(nr - 1) % 2] - j) dq.pop_back(); // get outta here
                // acuma sa scot de la inceput ca e in plus
                dq.push_back(j);
                while(!dq.empty() && dq.front() < j - vf[nr]) dq.pop_front();

                dp[j * nr + r][nr % 2] = dp[dq.front() * nr + r][(nr - 1) % 2] + (j - dq.front());
            }
        }

        // cout << "nr = " << nr << " dp = ";
        // for(int i = 0; i <= g; i++) cout << dp[i][nr % 2] << " ";
        // cout << '\n';
    }

    int last = g;
    while(dp[last][0] == INT_MAX) last--;
    out << last << " " << dp[last][0] << '\n';

    imparte_si_cucereste(1, 200, last);
    return 0;
}