CodeForces 1886E I Wanna be the Team Leader

洛谷传送门

CF 传送门

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

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

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

发现这个 dp 太蠢了。考虑直接把第一维塞进 dp 值中。重新设 fS 为已经考虑完的集合的编号集合为 S,若 [1,i] 被分配完了,i最小值。预处理出 gi,j 表示最小的 k 使得 akbikj,转移考虑枚举下一个集合是第 k 个,有 fS{k}gk,fS+1

g 可以双指针,因为 gi,jj 增大而不降。所以整个的复杂度就是 O((n+2m)m+nlogn)

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;
}
posted @   zltzlt  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示