题目大意
给出一个区间
[
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
n≤10;
对于
30
%
30\%
30%的数据,
n
≤
20
n \leq 20
n≤20;
对于
50
%
50\%
50%的数据,
n
≤
100
n \leq 100
n≤100;
对于
70
%
70\%
70%的数据,
n
≤
1
0
3
n \leq 10^3
n≤103;
对于
100
%
100\%
100%的数据,
n
≤
1
0
6
n \leq 10^6
n≤106,
a
i
≤
1
0
9
a_i \leq 10^9
ai≤109。
分析
这是一道用到贪心算法的题。在解题之前,我们需要先寻找一些结论。
我们考虑把这些数从小到大排序,然后考虑给这些数分组。因为排完序的数组满足
a
1
≤
a
2
≤
⋯
≤
a
n
a_1 \leq a_2 \leq \cdots \leq a_n
a1≤a2≤⋯≤an,所以当我们发现有一个数对
(
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)
(ax−1,ay),(ax−2,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
l≤ap1+ap3≤ap2+ap3,ap1+ap4≤ap2+ap4≤r,所以有
{
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。我们在处理的过程中会遇到如下几种情况:
- 当我们发现 x > y x>y x>y时,我们可以知道此时我们处理完了所有数。此时我们直接输出答案,退出程序即可。
- 当我们当我们发现 x = y x=y x=y时,我们可以知道此时我们处理剩了一个数。此时我们输出 答 案 + 1 答案+1 答案+1,然后退出程序。
- 当我们发现 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。
- 当我们发现 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。
- 当我们发现
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
ax−1,ax−2,⋯,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;
}
总结
这是一道需要一定思维能力的题,需要在分析后解决。