题意
对长度为\(N(1\leq N\leq5\times 10^5)\)的序列\(\{a_n\}(1\leq a_n \leq 10^9)\)进行分组,要求:
- 每一个\(a_i\)必须属于且只能属于一个组
- 每一个组内至少有\(K(K\leq N)\)个元素
- 每个组内元素值的极差\(\max \{|a_i-a_j|\}\leq D,(1\leq D\leq 10^9)\)
试问满足条件的分组存不存在
分析
最有可能合法的分组是在有序序列上的分划
不妨假设序列存在单调性:\(a_i \leq a_{i+1}\),现在证明非连续的分组必定不比连续的分组优。分组
\[S_1=\{a_{s_1},...,a_{m_1},a_{m_1},...,a_{t_1}\}, S_2=\{a_{m},a_{s_2},...,a_{t_2}\},s_2=t_1+1\]与分组
\[S_1^*=\{a_{s_1},...,a_{t_1-1}\}, S_2^*=\{a_{t_1},a_{s_2},...,a_{t_2}\}\]相比,显然有\[\#S_1=\#S_1^*\]且\[ \#S_2=\#S_2^*\]另外\[|a_{t_1}-a_{s_1}|+|a_{t_2}-a_m|\geq|a_{t_1-1}-a_{s_1}|+|a_{t_2}-a_{t_1}|\]故在分组大小相同的情况下,由于三角不等式,连续的分划更有可能是合法分组,下面我们讨论如何分划最优
枚举分划的右端点,查找合法的左端点
令\(dp[i]=1\)表示\(a_i\)与\(a_{i-1}\)之间的分划是合法的,即\(\{a_n\},n\leq i-1\)的合法分组存在。
反过来\(dp[i]=0\)表示\(a_i\)与\(a_{i-1}\)之间的分划不合法,即\(\{a_n\},n\leq i-1\)的合法分组不存在。
另外,记\(s[n]=\sum_{i=1}^n {dp[i]}\),即\(dp\)序列的前缀和序列。
边界条件
\(dp[0]=s[0]=1\)
转移方程
要求\(dp[n]\),就必须知道其对应的合法左端点在什么位置,由于\(K\)限制,不能太靠右,由于\(D\)限制,不能太靠左,而在某一个区间\([l,r]\)内的合法左端点都是可以继承的。
由于我们的分组是连续的,\(K\)限制下的\(r=n-K\)
又由于我们的序列是单调的,\(D\)限制下的\(l\)可以使用二分查找法找到
任务变成了在\(dp[l],...,dp[r]\)之间,是否有\(1\)的存在,即\(s[r]-s[l-1]\)是否为\(0\),即可
\[dp[n]=!!(s[r]-s[l-1])\] \[s[n]=s[n-1]+dp[n]\] 最后答案取决于\(dp[N+1]\)的值是否为\(1\)
复杂度
\(O(n\log n)\)
代码
/* Yuanjie Duane Ding (c) 2017
* any codes cannot be used for business
* BUAA开学季
* Templates for Codeforces special
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define NAME "985E"
using namespace std;
int N, K, D;
int av[500005], sn[500005];
void readin() {
scanf("%d%d%d", &N, &K, &D);
for(int i = 1; i <= N; ++i) {
scanf("%d", av+i);
}
}
void process() {
sort(av+1, av+1+N);
sn[1] = 1;
int l, r, tot;
for(int i = 1; i <= N; ++i) {
r = i-K+1;
l = lower_bound(av+1, av+1+N, av[i]-D)-av;
if(r < 1 || r < l || r > i) {
sn[i+1] = sn[i];
continue;
}
tot = sn[r]-sn[l-1];
sn[i+1] = sn[i]+(bool)tot;
#ifdef DEBUG
printf("%d: [%d, %d], tot %d\n", i, l, r, tot);
#endif
}
#ifdef DEBUG
for(int i = 1; i <= N+1; ++i) {
printf("%d: av %d; sn %d; val %d\n", i, av[i], sn[i], sn[i]-sn[i-1]);
}
#endif
if(sn[N+1] > sn[N]) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen(NAME ".in", "r", stdin);
#endif
readin();
process();
return 0;
}