QOJ17884 Same Segment 题解

link

结论:如果有解,那么只放 \(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;
}
posted @ 2026-08-28 11:36  cqbzcjh  阅读(6)  评论(0)    收藏  举报