#pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #include #include #include #include using namespace std; // 定数 const long long INF = 1e18; int M, N; vector A, B; vector ans; // 累積コストテーブル // SumDist[j][x] = sum_{p=0}^{x-1} |A[p] - B[j]| // これにより区間コストが O(1) で求まる vector> SumDist; // 手動Deque (定数倍高速化) struct FastDeque { int* q_idx; long long* q_val; int head, tail; int capacity; FastDeque(int size) { capacity = size + 5; q_idx = new int[capacity]; q_val = new long long[capacity]; head = 0; tail = 0; } // デストラクタ等省略(競プロ用途) inline void clear() { head = 0; tail = 0; } inline bool empty() { return head == tail; } inline void push_back(int idx, long long val) { q_idx[tail] = idx; q_val[tail] = val; tail++; } inline void pop_back() { tail--; } inline void pop_front() { head++; } inline long long back_val() { return q_val[tail - 1]; } inline int front_idx() { return q_idx[head]; } inline long long front_val() { return q_val[head]; } }; // 事前計算処理 void precompute_costs() { SumDist.assign(N, vector(M + 1, 0)); // 各 B[j] に対して、A の全要素との距離の累積和を計算 // A はソート済みなので、|A[p] - B[j]| の計算はポインタを進めながらやると速いが // ここは O(MN) かけて愚直にやっても全体に比べれば軽い for (int j = 0; j < N; ++j) { long long b_val = B[j]; long long current_sum = 0; SumDist[j][0] = 0; for (int x = 1; x <= M; ++x) { current_sum += abs(A[x-1] - b_val); SumDist[j][x] = current_sum; } } } // 固定された容量 k に対する最小コスト計算 // 計算量: O(MN) だが定数倍が非常に軽い long long calc(int k) { if (k <= 0) return INF; if (k > M) k = M; // 1次元DP配列 (prev -> curr) vector dp(M + 1, INF); dp[0] = 0; // バッファ vector next_dp(M + 1); // Deque (static にして再利用) static FastDeque dq(M + 5); for (int j = 0; j < N; ++j) { dq.clear(); // SumDist[j] への参照 (ポインタアクセスの最適化を期待) const vector& S = SumDist[j]; for (int x = 0; x <= M; ++x) { // 遷移: next_dp[x] = S[x] + min(dp[y] - S[y]) where x-k <= y <= x // 1. 新しい候補 (dp[x] - S[x]) を追加 if (dp[x] != INF) { long long val = dp[x] - S[x]; while (!dq.empty() && dq.back_val() >= val) { dq.pop_back(); } dq.push_back(x, val); } // 2. 範囲外 (index < x - k) を削除 if (!dq.empty() && dq.front_idx() < x - k) { dq.pop_front(); } // 3. 最小値で更新 if (!dq.empty()) { next_dp[x] = S[x] + dq.front_val(); } else { next_dp[x] = INF; } } dp = next_dp; } return dp[M]; } // 分割統治 void solve_dc(int l, int r) { if (l + 1 >= r) return; int mid = (l + r) / 2; ans[mid] = calc(mid); // 傾きチェック (l, mid, r) // (y2 - y1) * (x3 - x2) == (y3 - y2) * (x2 - x1) long long dy1 = ans[mid] - ans[l]; long long dx1 = mid - l; long long dy2 = ans[r] - ans[mid]; long long dx2 = r - mid; // 傾きが一致していれば線形補間 (D&Cの枝刈り) if (dy1 * dx2 == dy2 * dx1) { long long slope = dy1 / dx1; for (int i = l + 1; i < r; ++i) { if (i == mid) continue; ans[i] = ans[l] + slope * (long long)(i - l); } return; } solve_dc(l, mid); solve_dc(mid, r); } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); if (!(cin >> M >> N)) return 0; A.resize(M); for (int i = 0; i < M; ++i) cin >> A[i]; B.resize(N); for (int i = 0; i < N; ++i) cin >> B[i]; sort(A.begin(), A.end()); sort(B.begin(), B.end()); // 1. コストテーブルの前計算 precompute_costs(); ans.resize(M + 1); // 2. 上限の探索 (k=M のときは Greedy と等価) // 実は k がある程度大きくなるとコストは下がらなくなる。 // その境界を知るために k=M (無限扱い) を計算。 // ただし実装簡易化のため、単に [1, M] で D&C を行う。 // D&C の補間が効くため、後半が平坦なら高速にスキップされる。 ans[1] = calc(1); ans[M] = calc(M); solve_dc(1, M); for (int i = 1; i <= M; ++i) { cout << ans[i] << "\n"; } return 0; }