版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
正题
题面大意就是从每一个点开始往右走,可以走当且仅当下一个点权的两倍大于等于前面的最大值。
问每一个点出发的路径长度。
枚举i考虑什么样的点会在i点停下来,先让找到第一个在i点前面且比i的两倍大的第一个点。
这个东西可以用线段树来维护。
考虑在这里暂停的点一定会经过那个点,发现这个值是对一个区间产生贡献的,把整个复制为原来的三倍就可以了。
关于区间的事情用一个可删堆来维护就可以了。
#include<bits/stdc++.h>
#define ls now<<1
#define rs now<<1|1
using namespace std;
const int N=100010;
vector<int> A[N*3],D[N*3];
priority_queue<int,vector<int>,greater<int> > f;
bool we[N*3];
int a[N];
int mmax[N*3],ans[N];
int n,m;
void build(int now,int l,int r){
if(l==r) {mmax[now]=a[l];return ;}
int mid=(l+r)/2;
build(ls,l,mid);build(rs,mid+1,r);
mmax[now]=max(mmax[ls],mmax[rs]);
}
int get_mmax(int now,int x,int y,int l,int r){
if(x==l && y==r) return mmax[now];
int mid=(l+r)/2;
if(y<=mid) return get_mmax(ls,x,y,l,mid);
else if(mid<x) return get_mmax(rs,x,y,mid+1,r);
else return max(get_mmax(ls,x,mid,l,mid),get_mmax(rs,mid+1,y,mid+1,r));
}
int find_mmax(int now,int x,int y,int l,int r,int num){
if(r<l) return 0;
if(l==r) return mmax[now]>=num?l:0;
int mid=(l+r)/2;
if(y<=mid) return find_mmax(ls,x,y,l,mid,num);
else if(mid<x) return find_mmax(rs,x,y,mid+1,r,num);
else {
if(get_mmax(rs,mid+1,y,mid+1,r)>=num) return find_mmax(rs,mid+1,y,mid+1,r,num);
else return find_mmax(ls,x,mid,l,mid,num);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);ans[i]=1e9;
m=max(m,a[i]);
}
bool tf=true;
for(int i=1;i<=n;i++) if(a[i]*2<m) {tf=false;break;}
if(tf){
for(int i=1;i<=n;i++) printf("-1 ");
return 0;
}
build(1,1,n);
for(int i=1;i<=n;i++){
int x=find_mmax(1,1,i-1,1,n,2*a[i]+1);
if(!x) x=find_mmax(1,i+1,n,1,n,2*a[i]+1);
if(x){
if(x<i) A[n+x+1].push_back(2*n+i),D[2*n+x+1].push_back(2*n+i);
else A[x+1].push_back(2*n+i),D[n+x+1].push_back(2*n+i);
}
}
for(int i=1;i<=3*n;i++){
for(int j=0;j<A[i].size();j++) f.push(A[i][j]),we[A[i][j]]=true;
for(int j=0;j<D[i].size();j++) we[D[i][j]]=false;
while(!f.empty()){
int x=f.top();
if(!we[x]){
f.pop();
continue;
}
ans[i%n==0?n:i%n]=min(ans[i%n==0?n:i%n],x-i);
break;
}
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}