题解 [JOISC 2022] Super Dango Maker

传送门

讲课就是被带着颓题解

考虑挖掘这个操作的性质
操作是询问 \(s\) 中出现次数最少的元素的出现次数
那么发现 \(s\) 中出现次数最大的元素的出现次数也是可以求的
\(\overline s\) 问一下就行了
于是开 \(m\) 个桶,尝试将每个元素放到一个桶里
能放的条件是那个桶加入这个元素后出现次数最大的元素的出现次数 \(\leqslant 1\)
这样是 \(O(nm^2)\)
如果我们总是尝试将其放入编号尽可能小的桶里,就可以二分了
那么复杂度 \(O(nm\log m)\),可以通过

点击查看代码
#include "dango3.h"
#include <bits/stdc++.h>
using namespace std;
#define pb push_back

int n, m;
bool vis[10010];
vector<int> buc[30];

int Query(const vector<int>& a);
void Answer(const vector<int>& a);

int qmin(const vector<int>& a) {return Query(a);}
int qmax(const vector<int>& a) {
	vector<int> tem;
	for (int i=1; i<=n*m; ++i) vis[i]=1;
	for (auto& it:a) vis[it]=0;
	for (int i=1; i<=n*m; ++i) if (vis[i]) tem.pb(i);
	return m-Query(tem);
}
bool check(int id, int mid) {
	vector<int> tem; tem.pb(id);
	for (int i=1; i<=mid; ++i)
		for (auto& it:buc[i])
			tem.pb(it);
	return qmax(tem)<=mid;
}

void Solve(int n, int m) {
	::n=n; ::m=m;
	for (int i=1; i<=n*m; ++i) {
		int l=1, r=m, mid;
		while (l<=r) {
			mid=(l+r)>>1;
			if (check(i, mid)) r=mid-1;
			else l=mid+1;
		}
		buc[r+1].pb(i);
	}
	for (int i=1; i<=m; ++i) Answer(buc[i]);
}
posted @ 2022-06-22 15:41  Administrator-09  阅读(1)  评论(0编辑  收藏  举报