CF1793E Velepin and Marketi

首先发现,每个人过的题是不同的,所以我们不关心过了那些题目。

我们要直接去求分成 \(k\) 组最多的通过数是不容易的。我们可以转换一下,求当 \(i\) 个题通过的时候 __最多__分多少组。为什么记最多,因为化成 \(k\) 个组通过 \(i\) 题可以的情况下,我们可以任意合并两个组,保证依然可以通过 \(i\) 个题。相当于求了上界,方便求答案。

目前已知,要求:\(i\) 个题通过的时候 最多分多少组

我们考虑记两个内容 :

\(F_i\) :表示通过前 \(i\) 个题分成了多少组(只关心前 \(i\) 个被分成了多少组)。

\(G_i\) : 表示通过前 \(i\) 个题最少用了多少个人(因为会有后面的拿来充数)。

介绍一下定义,我们要求的其实是一个 \(ans\) 表示 \(i\) 个分组最多通过几道题。因为我求了 \(F_i\)\(G_i\),所以我们可以得到所有的人被分成了多少组。即 \((n - G_i) + F_i\)。将 \(ans_{(n - G_i) + F_i}\) 赋值为 \(i\),同时 \(ans_i = \max(ans_i,ans_{i-1})\)。这样就求出了 \(ans\) 数组。

当前可能 \(F_i\)\(G_i\) 记录的情况可能不止过了 \(i\) 道题。但在后面会重新被更改为最大的,所以不影响答案。

关于转移。我们知道将排序后连续一段放在一个组别中一定最优。因为当花费了 \(a_i\) 个人帮助 \(i\) 个人完成了题目,但 \(i-1\) 不在,所以有要花费 \(a_{i-1}\) 来完成第 \(i-1\) 个题目,这显然不优。其次,我们转移中,\(G_i\) 越小转移越优。因为 \(G_i\) 越大那么没用的越少,我们要求最多没用的一定是单个单个分开,所有 \(G_i\) 越大一定不比自己小的优。在 \(G_i\) 相同的情况下,\(F_i\) 越大越好,这就不多解释了。

转移还要分类讨论一下。

\(a_i > i\):找到 \(1 \sim (a_i - i)\) 中最小的 \(G_j\),最大的 \(F_j\)

\(a_i \le i\)\(F_i = 1,G_i = a_i\)

我们动态求前缀的最小的 \(G_j\),最大的 \(F_j\)

#include<bits/stdc++.h>
#define int long long
#define debug(x) cout<<#x<<"[:]"<<x,puts("");
#define FOR(i,a,b) for(ll i=(a); i<=(b); ++i)
#define ROF(i,a,b) for(ll i=(a); i>=(b); --i)
//
//
//
using namespace std;
inline ll read() {
	ll f = 0, t = 0;
	char c = getchar();
	while (!isdigit(c)) t |= (c == '-'), c = getchar();
	while (isdigit(c)) f = (f << 3) + (f << 1) + c - 48, c = getchar();
	return t ? -f : f;
}
void write(int x) {
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
const int MX = 3e5 + 10;
int a[MX];
int f[MX], g[MX];
int ans[MX], G[MX];
sky pa[MX];
const int inf = 1e18 + 10;
struct sky {
	int a,b,id;
};
sky merge(sky x,sky y) {
	if(x.a < y.a) return x;
	if(x.a > y.a) return y;
	if(x.a == y.a)
		if(x.b > y.b) return x;
		else return y;
}
signed main() {
	ios::sync_with_stdio(0), cout.tie(0);
	int n = read();
	int Q = read();
	FOR(i,1,n) a[i] = read();
	sort(a+1,a+n+1);
	FOR(i,1,n) g[i] = inf;
	FOR(i,1,n) {
		if(i - a[i] >= 1) {
			sky A = pa[i - a[i]];
			f[i] = A.b + 1;
			g[i] = A.a + A.id + a[i] + (i - a[i] - A.id);
		} else f[i] = 1,g[i] = a[i];
		sky B = {g[i] - i, f[i], i};
		pa[i] = merge(B,pa[i - 1]);
	}
	FOR(i,1,n) G[i] = (n - g[i]) + f[i];
	FOR(i,1,n) ans[G[i]] = i;
	ROF(i,n,1) ans[i] = max(ans[i + 1],ans[i]);
	FOR(i,1,Q) {
		int k = read();
		cout<<ans[k]<<" ";
	}
	return 0;
}

——end——

posted @ 2024-09-14 15:44  Sirkey  阅读(9)  评论(0编辑  收藏  举报