[ARC104] C - Fair Elevator

题面

题目大意

\(2n\) 个楼层和一部从 \(1\) 层到 \(2n\) 层单向运行一次的电梯。\(n\) 个人,第 \(i\) 个人会在 \(a_i\) 层上,\(b_i\) 层下。现有若干个值丢失,查询是否存在重新填数的方式使得:

  • \(a\)\(b\) 共同构成长为 \(2n\) 的排列。

  • \(\forall 1\leq i,j\leq n,[a_i,b_i]\cap [a_j,b_j]\neq \emptyset\),有 \(b_i-a_i=b_j-a_j\)

数据范围

  • \(1\leq n\leq 100\)

题解

首先,最后的方式肯定是由若干个不交的段组成,每段前半部分有人上,后半部分全下。

那么我们可以预处理并线性判断一个区间作为一个段是否合法。

接下来对数轴做可行性 DP 即可,时间复杂度 \(O(n^3)\)

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'
const int N=2e2+9;

int f[N],a[N],b[N],op[N],id[N],n;
inline bool Check(int l,int r){
    int len=r-l+1,mid=l+r>>1,hf=len>>1;
    if(len&1) return 0;
    for(int i=l;i<=mid;i++){
        if(!id[i]) continue ;
        if(op[i]==0){
            if(b[id[i]]!=-1&&b[id[i]]!=i+hf) return 0;
            else if(b[id[i]]==-1&&id[i+hf]) return 0;
        }else return 0;
    }
    for(int i=mid+1;i<=r;i++){
        if(!id[i]) continue ;
        if(op[i]==1){
            if(a[id[i]]!=-1&&a[id[i]]!=i-hf) return 0;
            else if(a[id[i]]==-1&&id[i-hf]) return 0;
        }else return 0;
    }
    return 1;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i]>>b[i];

    for(int i=1;i<=n;i++){
        if(a[i]!=-1){
            if(id[a[i]]){
                cout<<"No"<<endl;
                return 0;
            }
            op[a[i]]=0,id[a[i]]=i;
        }
        if(b[i]!=-1){
            if(id[b[i]]){
                cout<<"No"<<endl;
                return 0;
            }
            op[b[i]]=1,id[b[i]]=i;
        }
    }
    f[0]=1;
    for(int i=1;i<=(n<<1);i++){
        for(int j=1;j<=i;j++) f[i]|=f[j-1]&Check(j,i);
    }

    if(f[n<<1]) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;

    return 0;
}
posted @ 2025-04-08 15:15  JoeyJiang  阅读(28)  评论(0)    收藏  举报