【题解】CF1237D Balanced Playlist
题目分析:
对于第 \(i\) 个数其遇到第一个小于它一半的数就停止,而且这是个环所以就可以考虑二倍环长。
找小于它一半的数就上一个线段树就好了。
但是这样最后的答案其实相当于只考虑数 \(i\) 的限制,完全没有考虑 \(i\) 以后遇到的数的限制,所以设 \(ans[i]\) 为第 \(i\) 个数的答案的话,那么每次都需要 \(ans[i] = \min(ans[i],ans[i+1]+1)\)
(但是这样会发现样例二过不去
本质上就是因为我们原长的最后一个需要用倍长后的序列的第一个数的答案来更新,但是我们这样弄出来的第一个数的答案是不对的,所以三倍环长就好了。
无限播放就是任何一个数都不存在让他停止的数啦,就很简单可以判了。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
const int INF = 1e9+5;
int val[4 * N],ans[N],b[2*N],a[N],tot;
void build(int now,int now_l,int now_r){
val[now] = INF;
if(now_l == now_r) return;
int mid = (now_l + now_r)>>1;
build(now<<1,now_l,mid);build(now<<1|1,mid+1,now_r);
}
void modify(int now,int now_l,int now_r,int pos,int res){
if(now_l == now_r){
val[now] = res;
return;
}
int mid = (now_l + now_r)>>1;
if(pos <= mid) modify(now<<1,now_l,mid,pos,res);
if(pos > mid) modify(now<<1|1,mid+1,now_r,pos,res);
val[now] = min(val[now<<1],val[now<<1|1]);
}
int query(int now,int now_l,int now_r,int l,int r){
if(l > r) return INF;
if(l <= now_l && r >= now_r) return val[now];
int mid = (now_l + now_r)>>1,ans = INF;
if(l <= mid) ans = min(ans,query(now<<1,now_l,mid,l,r));
if(r > mid) ans = min(ans,query(now<<1|1,mid+1,now_r,l,r));
return ans;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n;scanf("%d",&n);
int mn = INF,mx = -INF;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);a[i+n] = a[i];a[i+2*n] = a[i];
b[++tot] = a[i];b[++tot] = (a[i] + 1) / 2;
mn = min(mn,a[i]),mx = max(mx,a[i]);
}
if(2 * mn >= mx){
for(int i=1; i<=n; i++) printf("-1 ");
return 0;
}
sort(b+1,b+tot+1);tot = unique(b+1,b+tot+1) - b - 1;
build(1,1,tot);
for(int i=3*n; i>=1; i--){
ans[i] = query(1,1,tot,1,lower_bound(b+1,b+tot+1,(a[i]+1)/2) - b - 1) - i;
modify(1,1,tot,lower_bound(b+1,b+tot+1,a[i]) - b,i);
if(i != 3*n) ans[i] = min(ans[i],ans[i+1] + 1);
}
for(int i=1; i<=n; i++) printf("%d ",ans[i]);
return 0;
}