[CEOI2011] Teams
Description
有
现在请你求出一种划分方案,在满足所有小朋友的要求的情况下,最大化队伍的数量。同时在此基础上,请你最小化人数最多的队伍的人数。
Solution
首先考虑
那么容易通过反证法证明,我们应该把其余
所以我们首先将所有人按
那么
容易发现一个单调性,当我们限制各组的人数时,组数会变少,这是由于 dp 方程变为了
其中
经过进一步思考之后,会发现每组最大人数也是可以 dp 出来的。
令
回顾
令
复杂度
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cassert>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e6 + 7;
int n, pre[N];
struct Node
{
int val, id;
bool operator <(const Node &X) const
{
if (val != X.val)
return val < X.val;
return id < X.id;
}
}a[N];
struct DP
{
int MinLen, dp;
}A[N];
vector<int> p[N];
struct FHQ_Node
{
int j, len, val, key;
int Maxj, Minlen;
int MinlenPos;
int ls, rs;
}t[N];
inline void updateMinLen(int x, int y)
{
if (t[x].Minlen > t[y].Minlen)
{
t[x].Minlen = t[y].Minlen;
t[x].MinlenPos = t[y].MinlenPos;
}
}
void update(int x)
{
t[x].Maxj = max(t[t[x].ls].Maxj, t[t[x].rs].Maxj);
t[x].Maxj = max(t[x].Maxj, t[x].j);
t[x].Minlen = t[x].len;
t[x].MinlenPos = t[x].j;
if (t[x].ls)
updateMinLen(x, t[x].ls);
if (t[x].rs)
updateMinLen(x, t[x].rs);
}
int merge(int x, int y)
{
if (!x || !y)
return x + y;
if (t[x].key < t[y].key)
{
t[x].rs = merge(t[x].rs, y);
update(x);
return x;
}
else
{
t[y].ls = merge(x, t[y].ls);
update(y);
return y;
}
}
void split(int id, int k, int &x, int &y)
{
if (!id)
x = y = 0;
else
{
if (t[id].val <= k)
x = id, split(t[id].rs, k, t[id].rs, y);
else
y = id, split(t[id].ls, k, x, t[id].ls);
update(id);
}
}
int NewNode(int j, int len)
{
static int nd_cnt = 0;
t[++nd_cnt] = (FHQ_Node){j, len, j + len, rand(), j, len, j, 0, 0};
// cout << "nd_cnt = " << nd_cnt << "\n";
return nd_cnt;
}
int main()
{
srand(114514);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].val;
a[i].id = i;
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)
if (i >= a[i].val)
p[i - a[i].val].push_back(i);
int rt = 0, x, y, z;
for (int i = 0, now = 0; i < n; i++)
{
if (!i || A[i].MinLen > 0)
{
z = NewNode(i, A[i].MinLen);
if (now < A[i].dp)
{
rt = z;
now = A[i].dp;
}
else if (now == A[i].dp)
{
split(rt, t[z].val, x, y);
rt = merge(merge(x, z), y);
}
}
for (int & k : p[i])
{
A[k].dp = now + 1;
split(rt, k, x, y);
A[k].MinLen = n;
if (x && A[k].MinLen > k - t[x].Maxj)
{
A[k].MinLen = k - t[x].Maxj;
pre[k] = t[x].Maxj;
}
if (y && A[k].MinLen > t[y].Minlen)
{
A[k].MinLen = t[y].Minlen;
pre[k] = t[y].MinlenPos;
}
rt = merge(x, y);
}
}
cout << A[n].dp << "\n";
int now = n;
while (now)
{
cout << now - pre[now] << " ";
assert(now > pre[now]);
for (int i = pre[now] + 1; i <= now; i++)
cout << a[i].id << " \n"[i == now];
now = pre[now];
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下