题解:P12502 「ROI 2025 Day1」天狼星的换班

分析

容易想到,定义 \(dp_i\) 为以第 \(i\) 个员工结尾时,前 \(i\) 个员工所能涉及到的房间能否被都修好。因为 \(t\)\(n\) 都比较大,要尽可能在 \(O(n\log n)\) 的时间内完成。

现在考虑如何转移,容易想到让第 \(i\) 个员工所管理的区间与前 \(i-1\) 个人拼,因为前 \(i-1\) 个人已经拼好,所以只要能拼上,那么 \(dp_i=1\)。那么要如何拼呢,考虑如下两幅图:

当我们枚举到 \(i=2\) 时,我们找到存在 \(m_2>r_1>l_2\),这说明我们可以先派 \(1\) 号员工出去,再派 \(2\) 号出去就可以把他们所涉及的房间都修完,我们称这种为正插。

考虑这幅图,跟上一张很像,但是如果在现在的情况下再用正插,那么因为 \(m_2\)\([l_1,r_1]\) 中,会被覆盖,就不能派去了。所以这里是一种新情况,存在 \(r_1>m_2\) 并且 \(m_1<l_2\) 那么就可以用反插,即先派 \(2\) 去再派 \(1\) 去即可。

可以证明,只有这两种情况。那么分清了情况,就考虑如何实现,因为要维护区间上的点存在情况和大小,容易想到可以用线段树。这里有一个比较妙的操作,线段树的 \(tree[i]\) 中 第 \(r_i\) 的位置存的是 \(m_i\) 的值,即 \(tree[r[i]]=m[i]\) 这样会方便比较。

为了前 \(i\) 个员工涉及的范围可以从小到大,所以要先对数组排序,以 \(m\) 为关键字从小到大排。假设现在枚举到第 \(i\) 条线段。那么对于第一个操作,需要查询 \([l_i-1,m_i-1]\) 中是否存在一个 \(r_j\) 即可,之所以是 \([l_i-1,m_i-1]\) 是因为当 \(r_j\)\(l_i-1\) 的位置时也可以完美拼上,而如果在 \(m_i\) 的话就会覆盖 \(m_i\) 所以不能在 \(m_i\)。对于第二个操作,需要判断 \([m_i,r_i-1]\) 中有没有一个 \(tree[r_j]<l_i\) 即可。
只要上述两种操作满足一点,那么这条线段就可以被插进去,即 \(dp_i=1\),同时也要把线段树中 \(tree[r[i]]\) 的值修改为 \(l_i\)

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2010101;
struct tt{
    int l,m,r;
}s[N];
bool cmp(tt a,tt b){return a.m<b.m;}
int f[N];
int t[N];
int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
void build(int p,int l,int r){
    t[p]=1e9;
    if(l==r)return ;
    int mid=l+r>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
}
void update(int p,int l,int r,int x,int k){
    t[p]=min(t[p],k);
    if(l==r)return ;
    int mid=l+r>>1;
    if(x<=mid)update(ls(p),l,mid,x,k);
    else update(rs(p),mid+1,r,x,k);
}
bool query(int p,int l,int r,int x,int y){
    if(x>y)return 0;
    if(x<=l&&r<=y){
        if(t[p]<1e8)return 1;
        return 0;
    }
    int mid=l+r>>1,ans=0;
    if(x<=mid)ans|=query(ls(p),l,mid,x,y);
    if(y>mid)ans|=query(rs(p),mid+1,r,x,y);
    return ans;
}
bool qry(int p,int l,int r,int x,int y,int k){
    if(x>y)return 0;
    if(x<=l&&r<=y){
        if(t[p]<k)return 1;
        return 0;
    }
    int mid=l+r>>1,ans=0;
    if(x<=mid)ans|=qry(ls(p),l,mid,x,y,k);
    if(y>mid)ans|=qry(rs(p),mid+1,r,x,y,k);
    return ans;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--){
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=k;i++){
            cin>>s[i].l>>s[i].m>>s[i].r;
        }
        sort(s+1,s+k+1,cmp);
        f[0]=1;
        build(1,0,n);
        update(1,0,n,0,0);
        bool p=0;
        for(int i=1;i<=k;i++){
            f[i]=query(1,0,n,s[i].l-1,s[i].m-1)|qry(1,0,n,s[i].m,s[i].r-1,s[i].l);
            if(f[i])update(1,0,n,s[i].r,s[i].m);
            if(s[i].r==n&&f[i]==1)p=1;
        }
        cout<<(p?"YES":"NO")<<'\n';
    }
}
posted @ 2025-09-10 21:37  一班的hoko  阅读(5)  评论(0)    收藏  举报