CodeForces 1886E I Wanna be the Team Leader

发布时间 2023-10-14 16:27:24作者: zltzlt

洛谷传送门

CF 传送门

把题意抽象成,给你长为 \(n\) 的序列 \(a\) 和长为 \(m\) 的序列 \(b\),初始有 \(m\) 个空集合(可重集),\(a\) 中的每个元素至多被分到 \(m\) 个集合中的一个。要求最后第 \(i\) 个集合 \(T_i\) 不为空,且 \(\forall x \in T_i, x \ge \frac{b_i}{|T|}\)。要求构造方案或报告无解。

先把 \(a\) 从大到小排序。发现最后每个集合分到的一定是在排序后序列的一段区间。因为 \(11121222\) 显然比 \(11112222\) 劣。

然后考虑一个从前往后分配集合元素的可行性 dp。设 \(f_{i, S}\) 为当前 \(a\)\([1, i]\) 的元素被分配完了,\(S\) 为已经考虑完的集合的编号集合为 \(S\)。转移枚举下一段的右端点 \(j\),枚举 \([i + 1, j]\) 被分到第 \(k\) 个集合,有 \(f_{j, S \cup \{k\}} \gets f_{i, S}\)。要求 \(a_j \ge \frac{b_k}{j - i}\)

发现这个 dp 太蠢了。考虑直接把第一维塞进 dp 值中。重新设 \(f_S\) 为已经考虑完的集合的编号集合为 \(S\),若 \([1, i]\) 被分配完了,\(i\)最小值。预处理出 \(g_{i, j}\) 表示最小的 \(k\) 使得 \(a_k \ge \frac{b_i}{k - j}\),转移考虑枚举下一个集合是第 \(k\) 个,有 \(f_{S \cup \{k\}} \gets g_{k, f_S + 1}\)

\(g\) 可以双指针,因为 \(g_{i, j}\)\(j\) 增大而不降。所以整个的复杂度就是 \(O((n + 2^m) m + n \log n)\)

code
// Problem: E. I Wanna be the Team Leader
// Contest: Codeforces - Educational Codeforces Round 156 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1886/problem/E
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;
const int maxm = (1 << 20) + 50;

ll n, m, b[25], g[25][maxn], f[maxm], p[maxm];
vector<int> ans[25];

struct node {
	ll x, i;
} a[maxn];

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i].x);
		a[i].i = i;
	}
	sort(a + 1, a + n + 1, [&](const node &a, const node &b) {
		return a.x > b.x;
	});
	for (int i = 1; i <= m; ++i) {
		scanf("%lld", &b[i]);
	}
	for (int i = 1; i <= m; ++i) {
		for (int j = 1, k = 1; j <= n; ++j) {
			k = max(k, j);
			while (k <= n && a[k].x * (k - j + 1) < b[i]) {
				++k;
			}
			g[i][j] = k;
		}
	}
	mems(f, 0x3f);
	f[0] = 0;
	for (int S = 0; S < (1 << m); ++S) {
		if (f[S] >= n) {
			continue;
		}
		for (int i = 1; i <= m; ++i) {
			if (S & (1 << (i - 1))) {
				continue;
			}
			int T = S | (1 << (i - 1));
			if (f[T] > g[i][f[S] + 1]) {
				f[T] = g[i][f[S] + 1];
				p[T] = i;
			}
		}
	}
	if (f[(1 << m) - 1] > n) {
		puts("NO");
		return;
	}
	puts("YES");
	int S = (1 << m) - 1;
	while (S) {
		int T = S ^ (1 << (p[S] - 1));
		for (int i = f[T] + 1; i <= f[S]; ++i) {
			ans[p[S]].pb(a[i].i);
		}
		S = T;
	}
	for (int i = 1; i <= m; ++i) {
		printf("%d ", (int)ans[i].size());
		for (int x : ans[i]) {
			printf("%d ", x);
		}
		putchar('\n');
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}