UOJ177 新年的腮雷【二分答案】

给定 \(n\) 个正整数 \(a_i,\cdots,a_n\)\(m\) 个正整数 \(b_1,\cdots,b_m\),进行 \(\frac{n-1}{m-1}\) 次操作,每次选择 \(a\) 中的 \(m\) 个正整数 \(x_1,\cdots,x_m\),删去它们,加入 \(\max\{x_i+b_i\}\),求最后得到的数的最小值。

\(n,m\le 5\cdot 10^4\)\((m-1)\mid(n-1)\)\(a_i\le 10^8\)\(b_i\le 10^7\)


考虑二分答案并倒着做:设最后剩下 \(x\),每次操作选择 \(x\) 拆成 \(x-b_i\),问最后能不能对应地 \(\ge a_i\)

不妨将 \(a,b\) 都排序,考虑更一般的集合 \(S,T\)\(|S|\le |T|\)\(|S|\equiv|T|\pmod{m-1}\),问 \(S\) 能不能拆成 \(T\)

\(|S|=|T|\),则只需判断将 \(S,T\) 排序之后是否有 \(s_i\ge t_i\)

\(|S|=0\land |T|>0\),无解。

\(|S|<|T|\),则

  • \(\max S<\max T\),无解。
  • \(\max T\le \max S<b_1+\max T\),将 \(S\) 的最大元拆掉就不能对应了,所以与 \(T\) 的最大元对应的元素不能拆,找一个 lower_bound 匹配即可。
  • \(\max S\ge b_1+\max T\),则 \(S\) 的最大元拆掉不劣。

模拟这个过程即可,时间复杂度 \(O(n\log n\log V)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 50003;
template<typename T>
void rd(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, m, a[N], b[N];
multiset<int> S;
multiset<int>::iterator it;
bool check(int x){
	S.clear(); S.insert(x);
	int now = n;
	while(!S.empty() && S.size() < now){
		int tmp = *--(it = S.end());
		if(tmp >= a[now] + b[1]){
			S.erase(it);
			for(int i = 1;i <= m;++ i)
				S.insert(tmp-b[i]);
		} else if(tmp >= a[now])
			S.erase(S.lower_bound(a[now--]));
		else return false;
	}
	if(S.size() == now){
		for(int i = 1;i <= now;++ i, S.erase(it))
			if(*(it = S.begin()) < a[i]) return false;
		return true;
	} return false;
}
int main(){
	rd(n); rd(m);
	for(int i = 1;i <= n;++ i) rd(a[i]);
	for(int i = 1;i <= m;++ i) rd(b[i]);
	sort(a + 1, a + n + 1);
	sort(b + 1, b + m + 1);
	int l = a[n], r = 260000000, md;
	while(l < r) check(md = l+r>>1) ? r = md : l = md+1;
	printf("%d\n", l);
}
posted @ 2021-06-25 18:07  mizu164  阅读(170)  评论(0)    收藏  举报