Pagini recente » Cod sursa (job #995295) | Cod sursa (job #2114015) | Cod sursa (job #395919) | Cod sursa (job #1045071) | Cod sursa (job #3336020)
#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");
// SURSA ASTA NU MERGE PE EXEMPLU IDK CUM DA 100
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;
}