#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <chrono>
#include <random>
#include <iomanip>
using namespace std;
// Cấu trúc lưu hộ gia đình
struct Item {
double a;
int id;
};
// Hàm xoá nhanh 1 phần tử khỏi vector (O(V))
void remove_from_group(vector<int>& vec, int val) {
for (int i = 0; i < (int)vec.size(); ++i) {
if (vec[i] == val) {
vec[i] = vec.back();
vec.pop_back();
return;
}
}
}
int main() {
// Tối ưu I/O
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N;
double L;
if (!(cin >> N >> L)) return 0;
vector<Item> items(N + 1);
vector<double> a(N + 1);
for (int i = 1; i <= N; ++i) {
cin >> items[i].a;
items[i].id = i;
a[i] = items[i].a;
}
// BƯỚC 1: SẮP XẾP VÀ QUY HOẠCH ĐỘNG (DP) ĐỂ TÌM BASELINE TỐT
sort(items.begin() + 1, items.end(), [](const Item& x, const Item& y) {
return x.a < y.a;
});
vector<double> dp(N + 1, 1e18);
vector<int> trace(N + 1, 0);
dp[0] = 0.0;
for (int i = 1; i <= N; ++i) {
for (int j = 0; j < i; ++j) {
double S = 0;
for (int m = j + 1; m <= i; ++m) S += items[m].a;
double H = S / L;
double cost = 0;
for (int m = j + 1; m <= i; ++m) {
cost += abs(H - items[m].a / H);
}
if (dp[j] + cost < dp[i]) {
dp[i] = dp[j] + cost;
trace[i] = j;
}
}
}
// Truy vết DP để lấy phân hoạch ban đầu
vector<int> initial_group(N + 1);
int curr = N;
int g_id = 0;
while (curr > 0) {
int prev = trace[curr];
for (int i = prev + 1; i <= curr; ++i) {
initial_group[items[i].id] = g_id; // Đưa về index ban đầu của hộ
}
g_id++;
curr = prev;
}
// BƯỚC 2: SIMULATED ANNEALING (SA)
vector<int> group_of = initial_group;
vector<vector<int>> members(N); // Tối đa N dải đất
vector<double> sum_group(N, 0.0);
vector<double> group_cost(N, 0.0);
for (int i = 1; i <= N; ++i) {
members[group_of[i]].push_back(i);
sum_group[group_of[i]] += a[i];
}
auto recalc_group = [&](int g) {
if (members[g].empty()) {
sum_group[g] = 0;
group_cost[g] = 0;
return;
}
double S = 0;
for (int u : members[g]) S += a[u];
sum_group[g] = S;
double H = S / L;
double c = 0;
for (int u : members[g]) c += abs(H - a[u] / H);
group_cost[g] = c;
};
double current_total_cost = 0;
for (int g = 0; g < N; ++g) {
recalc_group(g);
current_total_cost += group_cost[g];
}
double best_cost = current_total_cost;
vector<int> best_group_of = group_of;
mt19937 rng(1337); // Seed cố định để dễ debug
auto get_time = []() {
return (double)clock() / CLOCKS_PER_SEC;
};
double start_time = get_time();
double TIME_LIMIT = 4.8; // Chạy 4.8s để an toàn trong giới hạn 5s
double T_start = 100.0;
double T_end = 1e-6;
while (true) {
double elapsed = get_time() - start_time;
if (elapsed > TIME_LIMIT) break;
// Tính nhiệt độ hiện tại (giảm dần)
double progress = elapsed / TIME_LIMIT;
double current_T = T_start * pow(T_end / T_start, progress);
int type = rng() % 2; // 0: Move, 1: Swap
if (type == 0) {
// MOVE: Chuyển 1 phần tử sang nhóm khác
int u = uniform_int_distribution<int>(1, N)(rng);
int g_old = group_of[u];
int g_new = uniform_int_distribution<int>(0, N - 1)(rng);
if (g_old == g_new) continue;
// Đánh giá nhanh O(|V|)
double cost_old_new = 0;
if (members[g_old].size() > 1) {
double H_old = (sum_group[g_old] - a[u]) / L;
for (int x : members[g_old]) {
if (x != u) cost_old_new += abs(H_old - a[x] / H_old);
}
}
double H_new = (sum_group[g_new] + a[u]) / L;
double cost_new_new = abs(H_new - a[u] / H_new);
for (int x : members[g_new]) {
cost_new_new += abs(H_new - a[x] / H_new);
}
double delta = (cost_old_new + cost_new_new) - (group_cost[g_old] + group_cost[g_new]);
// Xác suất chấp nhận
if (delta < 0 || exp(-delta / current_T) > uniform_real_distribution<double>(0.0, 1.0)(rng)) {
group_of[u] = g_new;
remove_from_group(members[g_old], u);
members[g_new].push_back(u);
recalc_group(g_old);
recalc_group(g_new);
current_total_cost += delta;
if (current_total_cost < best_cost) {
best_cost = current_total_cost;
best_group_of = group_of;
}
}
} else {
// SWAP: Đổi chỗ 2 phần tử ở 2 nhóm khác nhau
int u = uniform_int_distribution<int>(1, N)(rng);
int v = uniform_int_distribution<int>(1, N)(rng);
int g_old = group_of[u];
int g_new = group_of[v];
if (g_old == g_new) continue;
double H_old = (sum_group[g_old] - a[u] + a[v]) / L;
double cost_old_new = abs(H_old - a[v] / H_old);
for (int x : members[g_old]) {
if (x != u) cost_old_new += abs(H_old - a[x] / H_old);
}
double H_new = (sum_group[g_new] - a[v] + a[u]) / L;
double cost_new_new = abs(H_new - a[u] / H_new);
for (int x : members[g_new]) {
if (x != v) cost_new_new += abs(H_new - a[x] / H_new);
}
double delta = (cost_old_new + cost_new_new) - (group_cost[g_old] + group_cost[g_new]);
if (delta < 0 || exp(-delta / current_T) > uniform_real_distribution<double>(0.0, 1.0)(rng)) {
group_of[u] = g_new;
group_of[v] = g_old;
remove_from_group(members[g_old], u);
remove_from_group(members[g_new], v);
members[g_old].push_back(v);
members[g_new].push_back(u);
recalc_group(g_old);
recalc_group(g_new);
current_total_cost += delta;
if (current_total_cost < best_cost) {
best_cost = current_total_cost;
best_group_of = group_of;
}
}
}
}
// Đưa ra output
vector<vector<int>> final_groups(N);
for (int i = 1; i <= N; ++i) {
final_groups[best_group_of[i]].push_back(i);
}
int k_used = 0;
for (int i = 0; i < N; ++i) {
if (!final_groups[i].empty()) k_used++;
}
// In chi phí nhỏ nhất (có thể làm tròn tới 6 chữ số thập phân)
cout << fixed << setprecision(6) << best_cost << "\n";
// In ra cách chia: số nhóm k, sau đó mỗi nhóm in ra số hộ và id của các hộ
cout << k_used << "\n";
for (int i = 0; i < N; ++i) {
if (!final_groups[i].empty()) {
cout << final_groups[i].size();
for (int x : final_groups[i]) cout << " " << x;
cout << "\n";
}
}
return 0;
}