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,mle 5cdot 10^4)((m-1)mid(n-1))(a_ile 10^8)(b_ile 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_ige t_i)

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

(|S|<|T|),则

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

模拟这个过程即可,时间复杂度 (O(nlog nlog 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
", l);
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/14932087.html