CF1886E I Wanna be the Team Leader

Fire_Raku發表於2024-06-07

CF1886E I Wanna be the Team Leader

狀壓 dp

注意到每個專案的程式設計師水平都要大於等於要求值,那麼就相當於限制只與程式設計師最小值有關。

那麼考慮將 \(a\) 序列從小到大排序,那麼就有結論:每個專案的程式設計師都是一段連續的區間。考慮貪心去證這個結論,假如有一段同一個專案的程式設計師不是連續的,那麼將他們拼在一起並將其中其他專案的程式設計師往後堆,一定不劣,因為不影響當前專案的最小值,還讓其他專案的最小值更大。

考慮狀壓 dp。跟 CF1550E Stringforces 的狀態表示一樣,預處理也一樣。設 \(f_s\) 表示完成專案集合為 \(s\) 的最短字首。預處理 \(g_{i,j}\) 表示從 \(j\) 位置開始,滿足專案 \(i\) 的最小右端點。那麼轉移就是

\[f_{s|2^i}=\min g_{i,f_s+1} \]

難點在方案的輸出,記錄轉移點,利用 \(g\) 陣列找到每一次選擇的區間。

那麼就做完了。複雜度 \(O(m2^m)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10, M = 21;
int n, m, lim;
pii a[N], ans[M];
int b[M];
int g[M][N], f[1 << M], pos[1 << M];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i].fi;
		a[i].se = i;
	}
	for(int i = 0; i < m; i++) {
		std::cin >> b[i];
	}
	std::sort(a + 1, a + n + 1, [&](pii a, pii b) {
		return a.fi < b.fi;
	});

	for(int i = 0; i < m; i++) {
		g[i][n + 1] = n + 1;
		for(int j = n; j >= 1; j--) {
			g[i][j] = std::min(g[i][j + 1], std::min(n + 1, j + ((b[i] + a[j].fi - 1) / a[j].fi) - 1));
		}
	}

	lim = (1 << m) - 1;
	for(int s = 0; s <= lim; s++) f[s] = n + 1;
	f[0] = 0;
	for(int s = 0; s < lim; s++) {
		if(f[s] > n) continue;
		for(int i = 0; i < m; i++) {
			if(!(s & (1 << i))) {
				if(f[s | (1 << i)] > g[i][f[s] + 1]) {
					f[s | (1 << i)] = g[i][f[s] + 1];
					pos[s | (1 << i)] = i;
				}
			}
		}
	}

	if(f[lim] > n) {
		std::cout << "NO\n";
		return 0;
	}

	while(lim) {
		for(int i = f[lim ^ (1 << pos[lim])] + 1; i <= f[lim]; i++) {
			if(g[pos[lim]][i] != g[pos[lim]][i + 1]) {
				ans[pos[lim]] = {i, f[lim]};
				break;
			}
		}
		lim = lim ^ (1 << pos[lim]);
	}

	std::cout << "YES\n";
	for(int i = 0; i < m; i++) {
		std::cout << ans[i].se - ans[i].fi + 1 << " ";
		for(int j = ans[i].fi; j <= ans[i].se; j++) {
			std::cout << a[j].se << " \n"[j == ans[i].se];
		}
	}
	return 0;
}

相關文章