CF713E

题目要求的这个东西显然是单调的,于是我们先套上一个二分。现在的问题就是每个点可以向左或向右延伸一个 \(x\) 的长度,是否能覆盖整个环。

这个问题我们现在还不会做,但是很容易秒掉它的序列版本,设 \(f_i\) 表示前 \(i\) 条线段能覆盖的最长长度,易于得到三条转移:

\(f_i=\max(f_i,a_i+x)(f_{i-1}\ge a_i-1)\):可以和前面的拼起来并向右延伸。

\(f_i=\max(f_i,a_i)(f_{i-1}\ge a_i-x-1)\):和前面不够的长度补起来。

\(f_i=\max(f_i,a_{i-1}+x)(f_j\ge a_i-x-1,j<i-1)\):和前面某一段拼起来,解放当前线段前面的一段,让他向右延伸。

显然最后一种转移只用考虑 \(j=i-2\),因为根据定义这个 \(f\) 是单调的。于是我们在 \(O(n)\) 的复杂度内解决了这个问题。

考虑环,发现这个 dp 跟距离有关系,人类智慧地,我们想到把这个环旋转,让彼此距离最长的两个点为初始和最终点,那么对于一个 \(x\),如果他大于等于最长距离,那么全部点都向右就可以覆盖整个环,否则 \(1\) 号和 \(n\) 号出发的线段就一定不会超出去,于是我们可以套用刚才的序列 dp。

枚举第一个点的状态,如果向左那么初始状态中 \(f_1=0\),否则 \(f_1=x,f_2=\max(b_2,x)\)

时间复杂度 \(O(n\log n)\)

#include<iostream>
#include<algorithm>
int n, m, a[200010], b[200010], f[200010];
bool ck(int x){
    for(int i = 1; i <= n; i++) f[i] = 0;
    f[1] = 0; for(int i = 2; i <= n; i++){
        f[i] = f[i - 1];
        if(f[i - 1] >= b[i] - 1) f[i] = std::max(f[i], b[i] + x);
        if(f[i - 1] >= b[i] - x - 1) f[i] = std::max(f[i], b[i]);
        if(i > 2 && f[i - 2] >= b[i] - x - 1) f[i] = std::max(f[i], b[i - 1] + x);
    } if(f[n] >= m - x - 1) return 1; f[2] = std::max(b[2], x);
    for(int i = 3; i <= n; i++){
        f[i] = f[i - 1];
        if(f[i - 1] >= b[i] - 1) f[i] = std::max(f[i], b[i] + x);
        if(f[i - 1] >= b[i] - x - 1) f[i] = std::max(f[i], b[i]);
        if(i > 2 && f[i - 2] >= b[i] - x - 1) f[i] = std::max(f[i], b[i - 1] + x);
    } if(f[n] >= std::min(m - 1, m + b[2] - x - 1)) return 1; return 0;
}
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    std::cin >> m >> n; if(n == 1) return std::cout << m-1, 0;
    for(int i = 1; i <= n; i++) std::cin >> a[i], a[i+n] = a[i]+m;
    std::sort(a + 1, a + n * 2 + 1); int p = 1, l = 0, r, rs;
    for(int i = 2; i <= n; i++) if(a[p+1]-a[p]<a[i+1]-a[i]) p=i;
    r = a[p+1] - a[p] - 1; int c = a[1 + p];
    for(int i = 1; i <= n; i++) b[i] = a[i + p] - c;
    while(l <= r){
        int md = l + r >> 1;
        if(ck(md)) rs = md, r = md - 1;
        else l = md + 1;
    } std::cout << rs;
}

upd.好像有点东西没说清楚,关于 dp 中第一个点向右延伸的初始状态设置。

考虑我们跑这个 dp 的条件是 \(x\) 比环上最长的长度短,那么我们选择了让第一个点向右,就算最后一个点向右,也不能覆盖整个环,所以我们只能让第二个点向左来补缺(显然在此处选第二个点比选后面的点更优秀)。因此 \(f_2\) 的初值是 \(\max(a_i,x)\),而判定合法的界在此处是 \(\min(m-1,m+b_2-x-1)\)

posted @ 2024-07-24 21:25  xlpg0713  阅读(1)  评论(0编辑  收藏  举报