QOJ17884 Same Segment 题解
结论:如果有解,那么只放 \(0\) 和 \(k\) 一定可以构造出一种解。这样问题就简化了很多,变成了一个选点问题。
假设现在已经选了一些点,其中最靠右的点是 \(p\),思考满足什么条件可以选择当前点 \(i\)(\(i>p\))。
对于右端点小于 \(i\) 的区间,如果它的左端点也大于 \(p\),那么它就覆盖不到。所以 \(p\) 一定大于等于右端点小于 \(i\) 的区间的左端点的最大值。设这个最大值为 \(l\)。
对于右端点大于等于 \(i\) 的区间,\(p\) 必须小于这些区间的左端点的最小值,否则就会有区间被 \(p\) 和 \(i\) 两个点覆盖。设这个最小值减一为 \(r\)。
那么 \(i\) 只能从 \(p\in[l,r]\) 转移过来。
我们维护一个数组 \(R\),\(R_i\) 表示 \(1\sim i\) 中最靠右的可达点。
如果 \(R_r\ge l\),\(i\) 就是可达点,\(R_i\) 就等于 \(i\)。否则 \(R_i=R_{i-1}\)。这样我们就求出了哪些点是可达的。
最后我们从左端点最大值开始遍历,从随便一个可达点回溯输出方案就行。
#include<bits/stdc++.h>
using namespace std;
const int N=1.2e7+5,M=2e5+5;
int T,n,m,k,mx[N],mn[N],R[N],lst[N],maxn,ans[N];bool vis[N];
void solve(){
maxn=0;
cin>>n>>m>>k;
for(int i=0;i<=n+1;i++){
mn[i]=n+1;mx[i]=0;
ans[i]=lst[i]=R[i]=vis[i]=0;
}
for(int i=1;i<=m;i++){
int l,r;cin>>l>>r;
mx[r]=max(mx[r],l);
mn[r]=min(mn[r],l);
maxn=max(maxn,l);
}
for(int i=n;i>=1;i--)mn[i]=min(mn[i+1],mn[i]);
for(int i=1;i<=n;i++){
int l=mx[i-1],r=min(i-1,mn[i]-1);
mx[i]=max(mx[i-1],mx[i]);
if(l<=r&&R[r]>=l){
R[i]=i;
vis[i]=1;
lst[i]=R[r];
}else R[i]=R[i-1];
}
for(int i=maxn;i<=n;i++){
if(vis[i]){
int now=i;
while(now){
ans[now]=k;
now=lst[now];
}
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
cout<<"\n";
return;
}
}
cout<<"-1\n";
}
int main(){
// freopen("same.in","r",stdin);
// freopen("same.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)solve();
return 0;
}

浙公网安备 33010602011771号