9.13 模拟赛 T3

题意:有一个长度为 \(n\) 的数组 \(b\),初始值全为 \(0\)。同时有一个长度为 \(m\) 的序列 \(a_i\)。依次进行操作 \(i=1,2,\dots,n\)

  • 对于操作 \(i\),可以选择 \(b\) 中任意不同的 \(a_i\) 个位置 \(j_1,j_2,\dots,j_{a_i}\),对于每个 \(p=1,2,\dots,a_i\),将 \(b_{j_p}\) 加一。

对于每一个 \(k=1,2,\dots,m\),求出,所有可能情况中,操作完所有 \(a_i\)后,\(b\) 数组中最多有多少个值为 \(k\) 的数。
\(1 \le n,m \le 2\times 10^5,1 \le a_i \le n\)

先考虑 \(O(nm)\) 怎么做。
直接考虑对于每个 \(k\),暴力地判断每个 \(x\),是否可能出现 \(k\) 次。这两层循环是 \(O(nm)\) 的,想想 \(O(1)\) 判断。

这里判断合法有三个限制。
直观一点,把 \(a_i\) 的取值画成一个矩形区域(直方图?),看成在里面填数的过程。

这里就可以直观地看到,左侧 \(k \times x\) 的矩形一定要填满,右侧 \(m \times (n-x)\) 的矩形可以任意填。

三个限制:

  • \(a_i\) 的总和不能超过总面积。
  • 能够填满 \(k \times x\) 的矩形。更具体地,\((x \cdot \sum[a_i > x]) + \sum_{a_i \le x} a_i \ge kx\)。也就是,不能将 \(a_i > x\) 的操作,一次全塞在 \(k\times x\) 的矩形里面。
  • 所有大于 \(n-x\)\(a_i\),【溢出】部分之和不能超过 \(kx\)

直观感受就是,满足这三个条件,就可以任意构造出来一组解。解的构造就是,先让每个 \(a_i\) 都在右侧矩形里待着,【溢出】部分就往左边矩形里放。如果不够,再从右侧矩形里面拿来一些放进左侧矩形里。

这样预处理前缀和,就可以做到 \(O(nm)\)

再考虑优化。
如果记 \(sum_j\) 为所有小于等于 \(j\) 的个数,\(cnt_j\) 为所有大于等于 \(j\) 的个数,那么,上面条件通过变形可以得到

\[\max(sum_n - m(n-x), sum_n - sum_{n-x} - (n-x)\cdot cnt_{n-x+1}) \le kx \le cnt_x\cdot x + sum_{x-1} \]

然后发现对于每个 \(x\),合法的 \(k\) 是一段区间!!!并可以 \(O(1)\) 计算!!!

然后就可以不动脑子地线段树维护区间赋值。时间复杂度 \(O(n \log m)\)

#include <bits/stdc++.h>
using namespace std;

// #define filename "seq" 
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
//#define multi_cases 1

#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int> 
#define ull unsigned long long
#define all(v) v.begin(), v.end()
#define upw(i, a, b) for(int i = (a); i <= (b); ++i)
#define dnw(i, a, b) for(int i = (a); i >= (b); --i)

template<class T> bool vmax(T &a, T b) { return b > a ? a = b, true : false; }
template<class T> bool vmin(T &a, T b) { return b < a ? a = b, true : false; }
template<class T> void clear(T &x) { T().swap(x); }

const int N = 2e5+2;

#define int long long

int n, m;
int a[N];

namespace SegmentTree {
	struct node {
		node *l, *r;
		int val;
		int tag;
		void upd(int v) { val = v, tag = v; }
		void down() { if(tag) l->upd(tag), r->upd(tag), tag = 0; }
	} pool[N << 1], *tmp = pool;
	
	node *newnode() {
		tmp->l = tmp->r = NULL;
		tmp->val = tmp->tag = 0;
		return tmp++;
	}
	
	struct Sgt {
		node *root;
		int l, r;
		void build(node *&p, int l, int r) {
			p = newnode();
			if(l == r) return;
			int mid = l + r >> 1;
			build(p->l, l, mid), build(p->r, mid+1, r);
		}
		void build(int l, int r) {
			this->l = l, this->r = r;
			build(root, l, r);
		}
		
		void update(node *p, int l, int r, int ql, int qr, int v) {
			if(l == ql && r == qr) return p->upd(v);
			int mid = l + r >> 1;
			p->down();
			if(mid >= qr) update(p->l, l, mid, ql, qr, v);
			else if(mid < ql) update(p->r, mid+1, r, ql, qr, v);
			else {
				update(p->l, l, mid, ql, mid, v);
				update(p->r, mid+1, r, mid+1, qr, v);
			}
		}
		void update(int ql, int qr, int v) { update(root, l, r, ql, qr, v); }
		
		void print(node *p, int l, int r) {
			if(l == r) return printf("%lld ", p->val), void();
			int mid = l + r >> 1;
			p->down();
			print(p->l, l, mid), print(p->r, mid+1, r);
		}
		void print() { print(root, l, r); }
	};
}

SegmentTree::Sgt tr;

void WaterM() {
	cin >> n >> m;
	upw(i, 1, m) scanf("%lld", &a[i]);
	
	vector<long long> cnt(n+2), sum(n+2);
	upw(i, 1, m) ++cnt[a[i]], sum[a[i]] += a[i];
	upw(i, 1, n) sum[i] += sum[i-1];
	dnw(i, n, 1) cnt[i] += cnt[i+1];
	
	tr.build(1, m);
	upw(x, 1, n) {
		int l = (max(sum[n] - m * (n-x), sum[n] - sum[n-x] - (n-x) * cnt[n-x+1]) + x - 1) / x;
		int r = (cnt[x] * x + sum[x-1]) / x;
		vmax(l, 1ll), vmin(r, m);
		if(l <= r) tr.update(l, r, x);
	}
	tr.print();
	puts("");
}

signed main() {
#ifdef filename
	FileOperations();
#endif
	
	signed _ = 1;
#ifdef multi_cases
	scanf("%d", &_);
#endif
	while(_--) WaterM();
	return 0;
}


posted @ 2025-09-13 17:18  Water_M  阅读(5)  评论(0)    收藏  举报