CF1237D Balanced Playlist 题解
题意
给定一个环,求从每一个点出发要跑多久才会停止。
停止的条件为:跑过的路径上的最大数严格大于当前数的二倍时停止。
无法停止输出 \(-1\)。
做法
先考虑无解。显然如果序列中最小值的二倍大于等于最大值时一定无解,反之一定有解。
考虑暴力。显然可以想到对每个数依次枚举,可以有 \(O(n^2)\) 的时间复杂度,显然无法通过。
考虑优化。分析枚举过程,我们在枚举时会维护一个最大值 \(MAX\) ,可能会出现两种情况,一个是当前枚举到的位置比 \(MAX\) 大,我们应该更改 \(MAX\),二是当前走到最后,结束当前遍历。
考虑其他节点位置对答案均无影响,发现是一类类似于 RMQ 的东西。即找第一个满足条件的数。发现这个数满足区间最值的性质。
考虑二分位置。
考虑找到位置后的转移。分类讨论。如果情况一在前,则 \(ans_i=ans_j+j-i\),否则 \(ans_i=j-i\)。
考虑记忆化。
做完了。
CODE
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+100;
int n;
int a[N];
int minn=1e9,maxx=-1;
int ans[N];
struct ST{
int f[N][21];
void ST_prework(int id)
{
for(int i=1;i<=n;i++) f[i][0]=f[i+n][0]=a[i]*id;
int t=log(n*2)/log(2)+1;
for(int j=1;j<t;j++)
for(int i=1;i<=2*n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int ST_query(int l,int r)
{
int t=log(r-l+1)/log(2);
return max(f[l][t],f[r-(1<<t)+1][t]);
}
}Max,Min;
int MIN(int l,int r)
{
int x=a[l-1];
while(l<r)
{
int mid=l+r>>1;
if(Min.ST_query(l,mid)*-2>=x) l=mid+1;
else r=mid;
}
return l;
}
int MAX(int l,int r)
{
int x=a[l-1];
while(l<r)
{
int mid=l+r>>1;
if(Max.ST_query(l,mid)<x) l=mid+1;
else r=mid;
}
return l;
}
int solve(int x)
{
if(ans[x]) return ans[x];
int A=MIN(x+1,x+n),B=MAX(x+1,x+n);
if(A<B) return ans[x]=A-x;
return ans[x]=B-x+solve((B-1)%n+1);
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],a[i+n]=a[i],minn=min(minn,a[i]),maxx=max(maxx,a[i]);
if(minn*2>=maxx)
{
for(int i=1;i<=n;i++) cout<<-1<<" ";
return cout<<endl,0;
}
Max.ST_prework(1),Min.ST_prework(-1);
for(int i=1;i<=n;i++) ans[i]=solve(i);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}