题解: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';
}
}

浙公网安备 33010602011771号