《GMOJ-Junior-2470 【GDKOI2021 普及组 Day1】配对》题解

题目大意

给出一个区间 [ l , r ] [l,r] [l,r] n n n个非负整数 a 1 , a 2 , ⋯   , a n a_1,a_2, \cdots ,a_n a1,a2,,an,问最少要删去多少个数,才能使剩下的数两两配对,且每一对数的和在 [ l , r ] [l,r] [l,r]中。
对于 20 % 20\% 20%的数据, n ≤ 10 n \leq 10 n10
对于 30 % 30\% 30%的数据, n ≤ 20 n \leq 20 n20
对于 50 % 50\% 50%的数据, n ≤ 100 n \leq 100 n100
对于 70 % 70\% 70%的数据, n ≤ 1 0 3 n \leq 10^3 n103
对于 100 % 100\% 100%的数据, n ≤ 1 0 6 n \leq 10^6 n106 a i ≤ 1 0 9 a_i \leq 10^9 ai109

分析

这是一道用到贪心算法的题。在解题之前,我们需要先寻找一些结论。
我们考虑把这些数从小到大排序,然后考虑给这些数分组。因为排完序的数组满足 a 1 ≤ a 2 ≤ ⋯ ≤ a n a_1 \leq a_2 \leq \cdots \leq a_n a1a2an,所以当我们发现有一个数对 ( a x , a y ) (a_x,a_y) (ax,ay)满足 a x + a y > r a_x+a_y>r ax+ay>r,那么 ( a x , a y + 1 ) , ( a x , a y + 2 ) , ⋯   , ( a x , a n ) (a_x,a_{y+1}),(a_x,a_{y+2}), \cdots ,(a_x,a_n) (ax,ay+1),(ax,ay+2),,(ax,an)都满足 数 对 和 > r 数对和>r >r;当我们发现有一个数对 ( a x , a y ) (a_x,a_y) (ax,ay)满足 a x + a y < l a_x+a_y<l ax+ay<l,那么 ( a x − 1 , a y ) , ( a x − 2 , a y ) , ⋯   , ( a 1 , a y ) (a_{x-1},a_y),(a_{x-2},a_y), \cdots ,(a_1,a_y) (ax1,ay),(ax2,ay),,(a1,ay)都满足 数 对 和 < l 数对和<l <l。同时,我们可以发现当 p 1 < p 2 < p 3 < p 4 p_1<p_2<p_3<p_4 p1<p2<p3<p4时,若 { a p 1 + a p 3 , a p 2 + a p 4 } ∈ [ l , r ] \{ a_{p_1}+a_{p_3},a_{p_2}+a_{p_4} \} \in [l,r] {ap1+ap3,ap2+ap4}[l,r],那么有 l ≤ a p 1 + a p 3 ≤ a p 2 + a p 3 , a p 1 + a p 4 ≤ a p 2 + a p 4 ≤ r l \leq a_{p_1}+a_{p_3} \leq a_{p_2}+a_{p_3},a_{p_1}+a_{p_4} \leq a_{p_2}+a_{p_4} \leq r lap1+ap3ap2+ap3,ap1+ap4ap2+ap4r,所以有 { a p 1 + a p 4 , a p 2 + a p 3 } ∈ [ l , r ] \{ a_{p_1}+a_{p_4},a_{p_2}+a_{p_3} \} \in [l,r] {ap1+ap4,ap2+ap3}[l,r]
由此,我们可以在给 a 1 , a 2 , ⋯   , a n a_1,a_2, \cdots ,a_n a1,a2,,an排序后,用两个指针 x , y x,y x,y求出答案。初始时令 x = 1 , y = n x=1,y=n x=1,y=n,然后我们考虑向右移动 x x x,向左移动 y y y。我们在处理的过程中会遇到如下几种情况:

  1. 当我们发现 x > y x>y x>y时,我们可以知道此时我们处理完了所有数。此时我们直接输出答案,退出程序即可。
  2. 当我们当我们发现 x = y x=y x=y时,我们可以知道此时我们处理剩了一个数。此时我们输出 答 案 + 1 答案+1 +1,然后退出程序。
  3. 当我们发现 a x + a y ∈ [ l , r ] a_x+a_y \in [l,r] ax+ay[l,r]时,我们发现我们找到了一个合法的数对。此时我们让 x x x 1 1 1,让 y y y 1 1 1
  4. 当我们发现 a x + a y > r a_x+a_y>r ax+ay>r时,我们可以确定 a y a_y ay一定是要被舍去的数。因为 a x + a y > r a_x+a_y>r ax+ay>r时, a y + 1 , a y + 2 , ⋯   , a n a_{y+1},a_{y+2}, \cdots ,a_n ay+1,ay+2,,an都无法与 a x a_x ax配对,且不存在 x ′ < x , y ′ > y x'<x,y'>y x<x,y>y,使得 { a x ′ + a y , a x + a y ′ } ∈ [ l , r ] \{ a_{x'}+a_{y},a_{x}+a_{y'} \} \in [l,r] {ax+ay,ax+ay}[l,r]。这时,我们把 a y a_y ay删去,将答案加 1 1 1,并使 y y y 1 1 1
  5. 当我们发现 a x + a y < l a_x+a_y<l ax+ay<l时,我们可以确定 a x a_x ax一定是要被舍去的数。与情况 4 4 4类似,因为 a x + a y < l a_x+a_y<l ax+ay<l时, a x − 1 , a x − 2 , ⋯   , a 1 a_{x-1},a_{x-2}, \cdots ,a_1 ax1,ax2,,a1都无法与 a y a_y ay配对,且不存在 x ′ > x , y ′ < y x'>x,y'<y x>x,y<y,使 { a x ′ + a y , a x + a y ′ } ∈ [ l , r ] \{ a_{x'}+a_{y},a_{x}+a_{y'} \} \in [l,r] {ax+ay,ax+ay}[l,r]。这时,我们把 a x a_x ax删去,将答案加 1 1 1,并使 x x x 1 1 1
    这样,我们就可以在 O ( n log ⁡ n ) O(n \log n) O(nlogn)的时间复杂度内解决本题了。

代码

根据我们的思路,可以写出如下代码:

#include<cstdio>
char buf[16777216];
inline int Read() //快速读入
{
	static char* c = buf;
	while (*c < '0' || *c>'9')
	{
		++c;
	}
	int ans = *c ^ 48;
	while (*(++c) >= '0' && *c <= '9')
	{
		ans = ans * 10 + (*c ^ 48);
	}
	return ans;
}
int a[1000001];
inline void QuickSort(const int l, const int r) //快速排序
{
	const int standard = (a[l] + a[r]) >> 1;
	int i = l, j = r;
	while (i <= j)
	{
		while (a[i] < standard)
		{
			++i;
		}
		while (a[j] > standard)
		{
			--j;
		}
		if (i <= j)
		{
			const int t = a[i];
			a[i] = a[j];
			a[j] = t;
			++i;
			--j;
		}
	}
	if (i < r)
	{
		QuickSort(i, r);
	}
	if (j > l)
	{
		QuickSort(l, j);
	}
	return;
}
int main()
{
	static_cast<void>(freopen("match.in", "r", stdin)); //定义文件输入输出
	static_cast<void>(freopen("match.out", "w", stdout));
	fread(buf, 1, 16777216, stdin); //读入数据
	const int n = Read(), l = Read(), r = Read(); //读入n,l,r
	for (int i = 1; i <= n; ++i) //读入a
	{
		a[i] = Read();
	}
	QuickSort(1, n); //对a排序
	int ans = 0;
	for (int i = 1, j = n; i <= j;) //统计答案,其中i对应分析中的x,j对应分析中的y
	{
		if (i == j) //情况2
		{
			++ans;
			break;
		}
		while (i < j && (a[i] + a[j] >= l && a[i] + a[j] <= r)) //情况3
		{
			++i;
			--j;
		}
		while (i < j && a[i] + a[j] > r) //情况4
		{
			++ans;
			--j;
		}
		while (i < j && a[i] + a[j] < l) //情况5
		{
			++ans;
			++i;
		}
	}
	printf("%d", ans);
	return 0;
}

总结

这是一道需要一定思维能力的题,需要在分析后解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值