分组配对(倍增+二分)

题目描述

【问题描述】
在一个班级中,恰好有 n n n名男生和 n n n名女生。为了便于管理,老师给这 n n n名男生和 n n n名女生分别从 1 1 1 n n n进行编号。某一天,老师在班级里开展了一项游戏。这个游戏需要将班级里的同学分成若干个不同的小组,且每个小组中需要同等数量的男生和女生。每个小组中,每一名男生需要和一名同小组的女生进行配对搭档,来完成游戏中的任务。一名男生不能和多名女生进行配对,一名女生也不能和多名男生进行配对。老师决定采用一种比较简便的方式对全班同学进行分组:按编号顺序进行划分。具体来说,老师会确定若干个区间 [ l 1 , r 1 ] , [ l 2 , r 2 ] ⋯ [ l k , r k ] [l_1,r_1],[l_2,r_2] \cdots [l_k, r_k] [l1,r1],[l2,r2][lk,rk],满足 l 1 = 1 , r k = n , l i = r i − 1 + 1 ( 1 < i ≤ k ) l_1 = 1,r_k=n,l_i=r_{i-1}+1(1<i \leq k) l1=1,rk=n,li=ri1+1(1<ik)编号处于同一个区间的男生和女生分为一组。
为了让游戏保证尽量公平,老师希望没有哪一个小组的实力过于突出。具体来说,每名同学都有一个正整数实力值,用来衡量一名同学的能力大小。当一名实力值为 a a a和一名实力值 b b b的女生进行配对时,这一对同学的实力值就会是 a ∗ b a * b ab。而一个小组的综合实力就是这个小组中每一对同学的实力值的和。为了保证不出现综合实力过高的小组,老师希望在分组后,不论小组内如何配对,每个小组的综合实力都不会超过一个给定值 M M M
最后,为了缩短游戏进行的时间,老师希望所分的小组数量尽可能少。你需要帮助他求出:在满足上述分组规则的情况下,最少的小组数量是多少?

【输入文件】
输入文件第一行为两个正整数 n , m n,m n,m
接下来两行,第一行为 n n n个正整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an其中 a i a_i ai表示编号为 i i i的男生的实力第二行为 n n n个正整数 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn,其中 b i b_i bi表示编号为 i i i的女生的实力值。

【输出文件】
输出一个整数,为最少的小组数量。输入数据保证至少存在一种满足分组
规则的分组方式。

【输入输出样例】

pair.in

3 50
6 7 2
6 3 5

pqir.out

2

题解

  • 对于每个小组来说能够得到最大综合实力的方式为:最大男生和最大的女生配对,次大的和次大的配对,以此类推。

  • 然后我们发现:在小组内新加入一对,那么小组最大综合能力一定上升显然

  • 根据贪心的思想,从编号为 1 1 1的开始向后考虑,尽可能的把人放在同一组,直到超过 m m m,再分下一组即可。

  • 那我们思考有没有什么方便的方法求最大综合实力的方式。倍增+二分!!

    对于每一组,考虑这组大小为 1 , 2 , 4 , ⋯   , 2 p 1,2,4,\cdots, 2^p 1,2,4,,2p时,最大综合实力值都没有超过 m m m,但组大小为 2 p + 1 2^{p+1} 2p+1,时最大综合实力值会超过 m m m,那么这组的最大大小应该在 [ 2 p , 2 p + 1 − 1 ] [2^p,2^{p+1}-1] [2p,2p+11]内。在此范围内进行二分即可。这样,当实际组大小是 k k k时,时间复杂度应为 O ( k l o g 2 k ) O(klog^2k) O(klog2k)。则总时间复杂度不超过 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

code

#include <bits/stdc++.h> 
using namespace std; 
const int maxn = 5e5 + 10; 
typedef long long LL; 

template <class T> 
inline void read(T &s) {
	s = 0; T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); } 
	while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); } 
	s *= w; 
}

int n; 
LL m; 
LL a[maxn], b[maxn]; 
LL c[maxn], d[maxn]; 

bool check(int l, int len) {
	int r = l + len - 1, tot = 0; 
	for (int i = l; i <= r; ++i) 
		c[++tot] = a[i], d[tot] = b[i]; 
	
	LL ret = 0ll; 
	sort(c + 1, c + tot + 1); 
	sort(d + 1, d + tot + 1); 
	
	for (int i = 1; i <= tot; ++i) {
		ret += c[i] * d[i]; 
		if (ret > m) return false; 
	}
	return true; 
}

int solve() {
	int L = 1, R = n, cnt = 0; 
	while (L <= R) {
		int len = 1; 
		for (; check(L, len) && L + len - 1 <= R; len <<= 1); 
		int l = len >> 1, r = len - 1; 
		while (l < r) {
			int mid = (l + r + 1) >> 1; 
			if (check(L, mid)) l = mid; 
			else r = mid - 1;  
		}
		L += l; cnt++; 
	}
	return cnt; 
}

#define FILE "pair" 

int main() {
	freopen(FILE".in", "r", stdin); freopen(FILE".out", "w", stdout); 
	
	read(n); read(m); 
	for (int i = 1; i <= n; ++i) read(a[i]); 
	for (int i = 1; i <= n; ++i) read(b[i]); 
	
	printf("%d\n", solve()); 
	return 0; 
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值