P10445「MYOI-R3」签到 官方题解
如果我们将签到点按其坐标从小到大排序,容易发现,对于第
第一步,记录原签到点在排序后对应的位置,设原第
第二步,枚举第
如果
-
如果能,将时间限制
+5 ,跳转至第三步。 -
否则,讨论单单往返一次第
i 个签到点会不会超时,会超时直接跳过本轮循环,跳转至第三步。
否则,则说明时间限制无法
第三步,用双指针维护出当前情况下能到达的最右边的签到点,这点也需要按
第四步,得出当前这组答案
第五步,当
-
如果使用二分,则二分查找第一个(即左指针坐标最小的)和最后一个(即左指针坐标最大的)包含此签到点的路径,将答案区间范围缩小(可以证明,答案区间范围一定连续)当然如果没有一条路径包含此签到点,就不能缩小答案区间范围。
-
但是我们能优化成双指针,因为答案区间范围中间不可能有空隙,假设原点左边有一组答案,则路径一定包含原点到其左指针的所有签到点,右边亦是如此,中间不可能有任何一条路径都不包含的签到点。
综上,总时间复杂度为
Std:(仅供参考)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
ll n=0;
int f=1;
char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9'){
n=(n<<3)+(n<<1)+(c^48);
c=getchar();
}
return n*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9) write(x/10);
putchar(x%10^48);
return;
}
struct node{
ll x;
int idx;
}s[1000002];
bool cmp(node a,node b){return a.x<b.x;}
vector<int> v;
int q[1000002];
int mat[1000002];
int e[1000002];
int main(){
int n=read();
ll m=read();
int p=read();
for(int i=1;i<=n;i++) s[i].x=read(),s[i].idx=i;
sort(s+1,s+1+n,cmp);
for(int i=1;i<=n;i++) mat[s[i].idx]=i;
int st=mat[p];
int ans=0;
int j=1;
for(int i=1;i<=n;i++){
int f=0;
if(i<=st){
if(s[st].x<=0 && (-s[i].x<<1)<=m+5) f=5;
if(s[i].x<=0 && 0<=s[st].x && (s[st].x-s[i].x<<1)<=m+5) f=5;
if(0<=s[i].x && (s[st].x<<1)<=m+5) f=5;
}
if(i==st+1) j=i;
if(f==0 && abs(s[i].x<<1)>m) continue;
j=max(i,j);
while(j<=n){
if(s[j].x<=0) j++;
else if(s[i].x<=0 && 0<=s[j].x && (s[j].x-s[i].x<<1)<=m+f) j++;
else if(s[i].x>=0 && (s[j].x<<1)<=m+f) j++;
else break;
}
j--;
if(j-i+1==ans) v.push_back(i);
if(j-i+1>ans){
v.clear();
ans=j-i+1;
v.push_back(i);
}
j++;
}
int cnt=0;
for(auto it:v) q[++cnt]=it;
int l=1,r=cnt;
for(int i=1;i<=n;i++){
e[i]=mat[i];
if(e[i]<q[l] || q[r]+ans-1<e[i]) continue;
while(l<r && q[l]+ans-1<e[i]) l++;
while(l<r && e[i]<q[r]) r--;
if(l==r) break;
}
write(ans);
putchar('\n');
for(int i=q[l];i<=q[l]+ans-1;i++) write(s[i].idx),putchar(' ');
return 0;
}