P9166题解
模拟大法好!
今年省选赛时看了旁边神仙一眼,第一题用了
简化题意
由于原题面有点复杂,因此我稍微化简题意:给定
解法
首先通过分析样例我们可以得到这样一张图:
(图丑请见谅)
看完这张图片也许你会有一个疑问,为什么 火车无法掉头,只能朝着一个方向运行,也就是说,火车是往
-
如果合法线段在点
x 的右侧,那么答案编号一定是它的右端点(因为方向是往右的) -
如果合法线段在点
x 的左侧,那么答案编号一定是它的左端点(因为方向是往左的) -
如果合法线段覆盖了点
x ,那么答案编号一定是它的左,右端点(因为火车从中间出发,方向可以往左也可以往右)
在处理完之后,我们就要把步骤退回来求合法线段了,我们假设你正在玩搭桥游戏,有很多木板可以搭桥,那么你搭桥的形状一定是这样的:
由上图我们发现每一块木板的端点一定是在上一块木板上,那么我们可以用这个思路寻找合法线段,首先定义
F1(i,1,n) if(a[i].l>=mini&&a[i].l<=maxi) maxi=max(maxi,a[i].r);//线段左端点位于合法区间内,该线段合法,更新右端点的最值
F2(i,n,1) if(a[i].r>=mini&&a[i].r<=maxi) mini=min(mini,a[i].l);//同上,但是需要倒推,因为线段排序后的上升性,后面有讲
(为了方便理解,将
当然,当某一条线段覆盖了
if(a[i].l<=x&&x<=a[i].r){//覆盖 x 号点
mini=min(mini,a[i].l);//取最值
maxi=max(maxi,a[i].r);
v[a[i].l]=v[a[i].r]=1;//标记合法
}
在完成以上处理之后,如果一条线段没有完全覆盖在
最后根据上面搭桥的那一张图片,我们发现从左到右的每一块木板的左端点和右端点一定是不下降的,首先来看一张图:
我们发现第
然后我们对线段进行排序后,就得到了下面这张图片:
此时第
最后输出时我们套用上第一步的样例结论做最后的特判即可(还需要标记数组给答案打标记,说明此点是合法线段的端点),后面是代码部分:
#include<bits/stdc++.h>
#define ll long long
#define F1(i,j,n) for(int i=j;i<=n;i++)
#define F2(i,j,n) for(int i=j;i>=n;i--)
using namespace std;
const int N=1e6+10,NN=1e4+10;
ll n,m,k,x,y,u,w,cnt=0,ans=0,t=0,l,r,len,T;
ll mini=INT_MAX,maxi=0,Mod;
bool v[N];
struct Node{
ll l,r;
}a[N];
bool cmp(Node a,Node b){
if(a.l==b.l) return a.r<b.r;
return a.l<b.l;
}
int main(){
cin>>m>>n>>x;
F1(i,1,n){
cin>>a[i].l>>a[i].r;//双端编号
if(a[i].l<=x&&x<=a[i].r){//覆盖x号点,即合法区间
mini=min(mini,a[i].l);//更新合法端点最值
maxi=max(maxi,a[i].r);
v[a[i].l]=v[a[i].r]=1;//线段两端标记为答案
}
}
sort(a+1,a+1+n,cmp);//排序
F1(i,1,n) if(a[i].l>=mini&&a[i].l<=maxi) maxi=max(maxi,a[i].r);//左端与合法线段有重合部分,更新最大值
F2(i,n,1) if(a[i].r>=mini&&a[i].r<=maxi) mini=min(mini,a[i].l);//右端与合法线段有重合部分,更新最大值
F1(i,1,n){
if(a[i].l<mini||a[i].r>maxi) continue;//不是合法线段
if(a[i].r<x) v[a[i].l]=1;//由结论得,该合法线段在x号点左边时,左端点是答案
else v[a[i].r]=1;//同上
}
F1(i,1,m) if(v[i]&&i!=x) cout<<i<<" ";//此编号标记为答案且不是起点
return 0;
}