当前位置: 代码迷 >> 综合 >> Balanced Playlist(Codeforces Global Round 5-D)(单调栈二分,树状数组)
  详细解决方案

Balanced Playlist(Codeforces Global Round 5-D)(单调栈二分,树状数组)

热度:43   发布时间:2023-11-19 10:22:07.0

题目

CF
有一个长度为 nnn (n≤105n\le10^5n105)的循环播放歌单,每首歌有一个优秀值 aia_iai? (ai≤109a_i\le 10^9ai?109)
听歌时选一首歌开始,如果某一首歌 xxx 的优秀值的两倍小于当前听过的歌中优秀值最大的,那么会在听完 xxx 之前停止听歌。
对于每首歌 iii,求 cic_ici? 表示如果从它开始听,最多听完几首歌,如果有重复的算多首。如果从一个位置开始能无限听下去,那么 ci=?1c_i=-1ci?=?1

思路

首先我们考虑破环成链,但是这里长度不是 2?n2*n2?n 而是 3?n3*n3?n ,可以观察这组数据:

4
3 2 5 3
5 4 3 6

我们发现对于第4个输出第一次经过2时可以,第二次最大值更新为5就不行了,可以理解为转一圈找最大值,然后又转一圈到最大值前一个(构造数据的话),于是答案最大为 2?n?22*n-22?n?2 (开始在最大值后,结束在最大值前,2圈)
然后我本来想这样干:
nextinext_inexti? 表示从 iii 开始以 aia_iai?maxmaxmax 值能扩展最远的下标,即 j∈[i,nexti]j\in [i, next_i]j[i,nexti?] 都满足 ai2≤aj\frac{a_i}{2}\le a_j2ai??aj? 最大的下标,考虑这张图:
在这里插入图片描述
我们可以于是我们可以将 [1,i][1,i][1,i] 的点最远点对 nextinext_inexti?minminmin 但是这是 n2n^2n2
这样我们就有几种处理方式:
1.倒着做
于是我们只要取 nextinext_inexti? 的最小值代表 iii 能到这个点最后减一个i再加1即可维护
2.分析单调性
如果记 pre[next[i]]=ipre[next[i]]=ipre[next[i]]=i ,我们答案就是单调递增的(没有减i),于是就用滑窗解决即可
而对于 prepreprenextnextnext 数组我们可以构建单调栈二分或者树状数组求解

代码

#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
    bool f=0;int x=0;char c=getchar();while(c<'0'||'9'<c){
    if(c=='-')f=1;c=getchar();}while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();return !f?x:-x;
}
#define MAXN 300000
#define INF 0x3f3f3f3f
int a[MAXN+5],pre[MAXN+5],S[MAXN+5];
int main(){
    //pre[i]:在i之前满足a[t]/2>a[i]最大tint n=read();for(int i=1;i<=n;i++)a[i]=a[i+n]=a[i+2*n]=read();int tp=0;a[0]=INF;for(int i=1;i<=3*n;i++){
    while(tp&&a[S[tp]]<=a[i]) tp--;S[++tp]=i;int L=0,R=tp+1;while(L+1<R){
    //实现时改了改pre定义int Mid=(L+R)>>1;if(a[S[Mid]]>2*a[i]) L=Mid;else R=Mid;}pre[i]=S[L];//printf("%d ",pre[i]);}//puts("");int p=1;for(int i=1;i<=n;i++){
    while(p<=3*n&&pre[p]<i) p++;//在可选取范围内//printf("%d %d\n",pre[p],i);if(p<=3*n) printf("%d ",p);//i<=pre[p] [i,p-1]else printf("-1 ");}return 0;
}
  相关解决方案