CF 985E (ECR)
题意
对长度为\(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)$
#代码
```c++
/* 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;
}
```\]
posted on 2018-05-23 21:57 No_CE_in_Vegetable 阅读(771) 评论(0) 编辑 收藏 举报