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;
}

posted @ 2025-05-09 20:21  袍蚤  阅读(17)  评论(0)    收藏  举报