CF1032G Chattering
Solution
能够显然的想到每个人传递必然是连续的,也就是对于一个 \(i\) ,他能够到达的区间为 \([i-a_i,i+a_i]\) 。
下一次,就会转移到 \([l,r]\) 中能往左和往右的最远端点,所以我们需要记录最值,那么不难想到用ST表去处理区间最值。
设 \(l_{i,j}\) 表示从 \(j\) 开始 \(2^i\) 秒能达到的最左端, \(r_{i,j}\) 同理。然后转移就是 \(l_{i,j}=\min\limits_{l_{i-1,j}\leq t\leq r_{i-1,j}}\{l_{i-1,t}\}\\r_{i,j}=\max\limits_{l_{i-1,j}\leq t\leq r_{i-1,j}}\{r_{i-1,t}\}\)
最后倍增求解即可。
大细节:因为是在一个环上,并且同时在两个方向走,所以要将环拆开,并复制成三份,然后再进行ST表等操作。
小细节:当区间覆盖超过 \(n\) 时,是完全覆盖,肯定有比选此时更优的解,跳过此时即可。
时间复杂度: \(O(n\log n)\) (好像比CF官方说的好一点?)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,a[N],b[N],c[N];
int Log[N];
int l[19][N],r[19][N];
struct rmq{
int st[N][19],val[N];
int type;
inline int Max(int x,int y){
return (val[x]<val[y])?y:x;
}
void build(int *b,int n,int _type){
type=_type;
for(int i=1;i<=n;i++) st[i][0]=i,val[i]=type*b[i];
for(int j=1;j<=Log[n];j++){
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=Max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
inline int query(int l,int r){
int k=Log[r-l+1];
return Max(st[l][k],st[r-(1<<k)+1][k]);
}
}rmq_l,rmq_r;
int main(){
scanf("%d",&n);
for(int i=2;i<=3*n;i++) Log[i]=Log[i>>1]+1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i+n]=a[i+n*2]=a[i];
}
if(n==1) return puts("0"),0;
for(int i=1;i<=3*n;i++){
b[i]=max(1,i-a[i]);
c[i]=min(3*n,i+a[i]);
}
for(int i=0;i<=Log[3*n];i++) r[i][3*n]=3*n,l[i][1]=1;
for(int i=1;i<=3*n;i++){
l[0][i]=b[i];
r[0][i]=c[i];
}
rmq_l.build(l[0],n*3,-1);rmq_r.build(r[0],n*3,1);
for(int i=1;i<=Log[3*n];i++)
for(int j=1;j<=3*n;j++){
int posl=rmq_l.query(l[i-1][j],r[i-1][j]);
int posr=rmq_r.query(l[i-1][j],r[i-1][j]);
l[i][j]=min(l[i-1][posl],l[i-1][posr]);
r[i][j]=max(r[i-1][posl],r[i-1][posr]);
}
for(int j=n+1;j<=n*2;j++){
int u=j,v=j;
int ans=0;
for(int i=Log[3*n];i>=0;i--){
if(max(r[i][v],r[i][u])-min(l[i][u],l[i][v])+1>=n) continue;
int nu=rmq_l.query(l[i][u],r[i][v]);
int nv=rmq_r.query(l[i][u],r[i][v]);
u=nu;v=nv;
ans+=1<<i;
}
ans++;
printf("%d ",ans);
}
puts("");
return 0;
}