分析
亂搞題。
右移若干次很顯然可以破環成鏈,然後 \(l,r\) 指標在這個長度為 \(2n\) 的序列上右移。每種狀態的答案就是 \([l,r]\) 的答案。
考慮相鄰兩種狀態的變化量。設上一次 \(l'\) 指向的值為 \(p_{l'}\),則到當前狀態的變化量就是:\(-\operatorname{mex}(p_{l'})-sum + cnt \times p_{l'}+n\)。第一項和最後一項很顯然,就是 \(l'\) 從開頭變到結尾了。第二項 \(sum\) 表示 \(\operatorname{mex}\) 值比 \(p_{l'}\) 大的和。因為在現在的前 \(n-1\) 項中 \(\operatorname{mex}\) 值是不可能超過 \(p_{l'}\) 的(\(p_{l'}\) 空出來了)。第三項的 \(cnt\) 就是比 \(p_{l'}\) 大的值的數量。
這個變化量用線段樹亂搞就行了,相當於是區間賦值。線上段樹上二分,統計一下最大值和最小值即可。最後的求某種狀態 \(\operatorname{mex}\) 值之和就是區間 \([l,r]\) 的和。複雜度 \(O(n\log n)\)。
程式碼
#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define re register
#define il inline
const int N=1e6+5,M=1e7+5;
int n,p[N];
bool cnt[N];
struct tree{
long long l,r,sum,lz,mx,mi;
}tr[M];
il void up(int now){
tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
tr[now].mx=max(tr[now<<1].mx,tr[now<<1|1].mx);
tr[now].mi=min(tr[now<<1].mi,tr[now<<1|1].mi);
return ;
}
il void down(int now){
if(tr[now].lz!=-1){
tr[now<<1].lz=tr[now<<1|1].lz=tr[now].lz;
tr[now<<1].sum=(tr[now<<1].r-tr[now<<1].l+1)*tr[now].lz;
tr[now<<1|1].sum=(tr[now<<1|1].r-tr[now<<1|1].l+1)*tr[now].lz;
tr[now<<1].mx=tr[now<<1|1].mx=tr[now].lz;
tr[now<<1].mi=tr[now<<1|1].mi=tr[now].lz;
tr[now].lz=-1;
}
return ;
}
il void build(int now,int l,int r){
tr[now].l=l,tr[now].r=r,tr[now].sum=0,tr[now].lz=-1,tr[now].mx=0,tr[now].mi=1e18;
if(l==r) return ;
int mid=l+r>>1;
build(now<<1,l,mid),build(now<<1|1,mid+1,r);
return ;
}
il void insert(int now,int l,int r,int k){
if(tr[now].l>=l&&tr[now].r<=r){
tr[now].lz=k,tr[now].sum=(tr[now].r-tr[now].l+1)*k;
tr[now].mx=k,
tr[now].mi=k;
return ;
}
down(now);
int mid=tr[now].l+tr[now].r>>1;
if(l<=mid) insert(now<<1,l,r,k);
if(mid<r) insert(now<<1|1,l,r,k);
up(now);return ;
}
il void insert2(int now,int l,int r,int k){
if(tr[now].l>=l&&tr[now].r<=r){
down(now);
if(tr[now].mi>k){
tr[now].lz=k,tr[now].sum=(tr[now].r-tr[now].l+1)*k;
tr[now].mx=tr[now].mi=k;return ;
}
if(tr[now<<1].mx>k) insert2(now<<1,l,r,k);
if(tr[now<<1|1].mx>k) insert2(now<<1|1,l,r,k);
up(now);return ;
}
down(now);
int mid=tr[now].l+tr[now].r>>1;
if(l<=mid&&tr[now<<1].mx>k) insert2(now<<1,l,r,k);
if(mid<r&&tr[now<<1|1].mx>k) insert2(now<<1|1,l,r,k);
up(now);return ;
}
il long long query(int now,int l,int r){
if(tr[now].l>=l&&tr[now].r<=r) return tr[now].sum;
down(now);
int mid=tr[now].l+tr[now].r>>1;
long long ans=0;
if(l<=mid) ans+=query(now<<1,l,r);
if(mid<r) ans+=query(now<<1|1,l,r);
up(now);
return ans;
}
il void solve(){
scanf("%d",&n);
for(re int i=1;i<=n;++i) scanf("%d",&p[i]);
build(1,1,n*2);int lst=0;
for(re int i=1;i<=n;++i){
cnt[p[i]]=1;
while(cnt[lst]) ++lst;
insert(1,i,i,lst);
}
for(re int i=0;i<=n;++i) cnt[i]=0;
long long maxx=query(1,1,n);
int l=1,r=n;
for(re int i=1;i<=n;++i){
int now=p[l];
int L=l+1,R=r;
insert2(1,L,R,now);
++l,++r;
insert(1,r,r,n);
maxx=max(maxx,query(1,l,r));
}
cout<<maxx<<"\n";return ;
}
signed main(){
int t;cin>>t;while(t--)
solve();
return 0;
}