【题解】 CF1237D 【Balanced Playlist】
FallenGemini · · 题解
题意
因为笔者太蒻了所以题意显得比较冗杂...
给定一个长度为
对于第
换句话说,对于以第
-
\forall~k\in\{i,i+1,i+2,\cdots,j-1\}$ 满足 $a_k\ge \dfrac{max\{a_i,a_{i+1},\cdots,a_{k-1}\}}{2} -
a_j< \dfrac{max\{a_i,a_{i+1},\cdots,a_{j-1}\}}{2}
我们需要求出从第
Solution
首先考虑遍历长度无限长的情况:既然是无限长,那么整个数列就会全部被遍历,也就是说数列中的最小值
考虑更一般的情况:对于任意一个数,我们从这个数开始向右遍历,在遍历的过程中我们会维护一个
- 情况一:当前节点的值比
Max 大,我们需要更新Max 。 - 情况二:当前节点的值严格小于
\frac{Max}{2} ,遍历停止。
除了以上两种情况,其他节点对这个位置均无影响,那么我们应该怎么快速求出这两种情况的最靠前的位置呢?(肯定不会写O(n)暴力吧,如果暴力还优化什么。。。
显然这是一类
例如:当前我们需要在数列
第一次二分:
第二次二分:
第三次二分:
此时
已经解决了怎么找到最近的满足条件的数,下面来考虑怎么转移:
若情况二靠前,则从该节点开始的答案显然为
若情况一靠前,则从该节点开始的答案显然
显然情况一的依赖关系不存在环,那记忆化搜索一遍就搞定了。
注意这道题是环,所以要破环为链。
Code
代码不压行,可读性较高
//压行课次
#include<bits/stdc++.h>
#define LL long long
#define LD long double
using namespace std;
const int MAXN=1e5+10;
int n,a[MAXN<<1],MX[MAXN<<1][19],MN[MAXN<<1][19],ans[MAXN],MXX,MNN=1e9;
int read(){int sss=0,fff=1;char ccc;ccc=getchar();while(ccc<'0'||ccc>'9'){if(ccc=='-')fff=-1;ccc=getchar();}while(ccc>='0'&&ccc<='9')sss=(sss<<1)+(sss<<3)+(ccc^'0'),ccc=getchar();return sss*fff;}
int MinST(int l,int r)
{
int len=r-l+1;
int k=log2(len);
if(a[MN[l][k]]<a[MN[r-(1<<k)+1][k]]) return MN[l][k];
return MN[r-(1<<k)+1][k];
}
int MaxST(int l,int r)
{
int len=r-l+1;
int k=log2(len);
if(a[MX[l][k]]>a[MX[r-(1<<k)+1][k]]) return MX[l][k];
return MX[r-(1<<k)+1][k];
}
int Min(int l,int r)
{
int x=a[l-1];
while(l<r)
{
int mid=(l+r)>>1;
if(a[MinST(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(a[MaxST(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;
else return ans[x]=B-x+solve((B-1)%n+1);
}
int main()
{
// freopen("123.in","r",stdin);
// freopen("123.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=a[i+n]=read(),MX[i][0]=MN[i][0]=i,MX[i+n][0]=MN[i+n][0]=i+n,MXX=max(MXX,a[i]),MNN=min(MNN,a[i]);
if(MNN*2>=MXX)
{
for(int j=1;j<=n;j++)
printf("-1 ");
return 0;
}
for(int k=1;k<=18;k++)
{
int len=1<<k;
for(int i=1;i+len-1<=n*2;i++)
{
int j=i+len-1,mid=i+(len/2);
if(a[MX[i][k-1]]<a[MX[mid][k-1]]) MX[i][k]=MX[mid][k-1];
else MX[i][k]=MX[i][k-1];
if(a[MN[i][k-1]]>a[MN[mid][k-1]]) MN[i][k]=MN[mid][k-1];
else MN[i][k]=MN[i][k-1];
}
}
for(int i=1;i<=n;i++)
ans[i]=solve(i);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}