zoukankan      html  css  js  c++  java
  • CF713E Sonya Partymaker

    洛谷传送门 CF传送门

    Description

    在一个长度为 (m) 的环上有指定的 (n) 个点,每个点可以选一个方向(左或右)延伸出一条长度为 (x) 的线段,问覆盖这个环的最小 (x)

    Solution

    因为要求最小的 (x) ,所以可以考虑二分答案。

    那么我们怎么 (operatorname{check}) 呢?

    因为是在一个环上,所以用熟悉的方法,先拆环成链,然后再返过来看环。

    如果在一个链上,我们就可以用DP了。

    (f_i) 为第 (i) 个点能覆盖的最远位置, (p_i) 为第 (i) 个点的位置,那么可以得出转移方程:

    [f_i=maxegin{cases}p_i+x &(p_i-1leq f_{i-1})\p_i&(p_i-x+1leq f_{i-1}<p_i-1)end{cases} ]

    第一个代表 (i-1) 能覆盖到 (i) 的位置,所以 (i) 可以往右走;第二个是 (i-1) 覆盖不到 (i) 但是 (i) 往左走能使得 (i) 之前被都被覆盖,所以 (i) 走的最远的位置就是原来的位置。

    但是这样会有个问题, (i-1)(i) 之间的位置如果可以用 (i-2) 往左走来覆盖,我们没有考虑这种情况。那么此时 (i-1) 就可以往右走,状态方程就会变成:

    [f_{i}=max(f_i,p_{i-1}+x) (p_{i}-x+1leq f_{i-2}) ]

    状态转移就完了。

    嗯? (i-1)(i) 之间的位置能被 (i+1) 覆盖,怎么办。

    那什么情况 (i+1) 会往左覆盖呢?就是 (f_{i}<p_{i+1}-1) ,所以如果 (i) 能往右,那 (i-1) 往右显然更优,所以是包含在上面的方程中的( ̄▽ ̄)"

    那么回过来考虑环怎么办——发现可能 (1)(2) 之间的这一段是由 (n) 来覆盖的,那这样就不能转移了。

    为了避免这种情况,我们就将最长的那一条边破开,并这条边作为二分的上界,那么现在的 (n) 就不能往右覆盖 (1) 了。

    然后将 (1) 往左和往右都DP一次即可。

    Code

    #include<bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    const int N=200010;
    ll n,m,a[N],p[N],f[N],l,r,ans;
    
    inline ll read(){
        ll x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
        return x*f;
    }
    
    inline void init(){
        int now=1;
        for(int i=2;i<=n;i++)
            if(a[i+1]-a[i]>a[now+1]-a[now]) now=i;
        r=a[now+1]-a[now]-1;
        for(int i=1;i<=n;i++) p[i]=a[i+now];
        ll tmp=p[1];
        for(int i=1;i<=n;i++) p[i]-=tmp;
    }
    
    inline bool check(ll x){
        f[1]=0;
        for(int i=2;i<=n;i++){
            f[i]=f[i-1];
            if(f[i-1]>=p[i]-1) f[i]=max(f[i],p[i]+x);
            if(f[i-1]>=p[i]-x-1) f[i]=max(f[i],p[i]);
            if(i>2&&f[i-2]>=p[i]-x-1) f[i]=max(f[i],p[i-1]+x);
        }
        if(f[n]>=m-x-1) return 1;
        f[2]=max(p[2],x);
        for(int i=3;i<=n;i++){
            f[i]=f[i-1];
            if(f[i-1]>=p[i]-1) f[i]=max(f[i],p[i]+x);
            else if(f[i-1]>=p[i]-x-1) f[i]=max(f[i],p[i]);
            if(f[i-2]>=p[i]-x-1) f[i]=max(f[i],p[i-1]+x);
        }
        if(f[n]>=min(m-1,m+p[2]-x-1)) return 1;
        return 0;
    }
    
    int main(){
        m=read(); n=read();
        for(int i=1;i<=n;i++){
            a[i]=read();
            a[i+n]=a[i]+m;
        }
        if(n==1){
            printf("%lld
    ",m-1);
            return 0;
        }
        init();
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Kafka学习笔记(五、Kafka偏移量)
    web-备份是个好习惯
    web-Web5
    web-web4
    web-flag就在index里
    C++日期类、时间类、日期时间类的设计
    蒙提霍尔问题简单粗暴的理解掉
    sql分组操作符与聚集函数
    sql增删改
    sql简单查询
  • 原文地址:https://www.cnblogs.com/jasony/p/13930361.html
Copyright © 2011-2022 走看看