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)\)

代码

/* 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;
}

猜你喜欢

转载自www.cnblogs.com/NoCEinVegetable/p/9079754.html