首先注意到这样一个性质:既然两个序列的平均值相同,并且又形成公差 \(\pm 1\) 的等差数列,就必然会存在一个元素 \(x\) 满足 \(x\) 在两个序列中都出现过(否则两个序列的值域区间不交,值域区间靠后的那个显然平均值比靠前的那个大)。那么我们枚举 \(x\) 在两个序列中出现的位置 \(p\) 和 \(q\)(\(p<q\))。当然,你直接枚举复杂度至少得 \(O(n^2)\) 了肯定爆掉了,考虑这样几个性质:
- \((p,q)\) 中间所有被选择的元素要么属于上升序列,要么属于下降序列。不妨假设 \(p\) 属于上升序列,\(q\) 属于下降序列,如果中间同时有属于两个序列的元素,那么必然存在 \(a_s=x+1\) 属于上升序列,\(a_t=x+1\) 属于下降序列,那么如果 \(s,t\) 这个符合条件我们改枚举 \(x+1\) 就行了,否则肯定还有 \(a_u=a_v=x+2\),以此类推。也就是说我们加上这个限制是不影响答案的。
- 不能存在 \(r\in(p,q)\) 满足 \(a_r=x\),否则根据上个结论,\((p,q)\) 中间选择的元素要么全部与 \(p\) 在同一个序列,要么全部与与 \(q\) 在同一个序列,不妨假设与 \(p\) 在同一个序列,那么我们把 \(q\) 的位置挪到 \(r\) 也是 ok 的,因为中间没有 \(q\) 所在的序列的元素,自然不影响这个序列的合法性。
换句话说,我们需要枚举的 \((p,q)\) 只有 \(O(n)\) 组,并且按照 \(p\) 属于上升还是下降序列 / 中间那一段是属于上升还剩下降序列总共可以分出四类:
- \(p\) 上升,\(q\) 下降,中间部分属于上升序列。
- \(p\) 上升,\(q\) 下降,中间部分属于下降序列。
- \(p\) 下降,\(q\) 上升,中间部分属于上升序列。
- \(p\) 下降,\(q\) 上升,中间部分属于下降序列。
但是这样问题又来了,中间的部分被强制要求属于同一个序列,这是好办的,但是 \(p\) 左边和 \(q\) 右边的部分是不确定的,而题目的限制条件又比较紧,尤其是那个选 \(k\) 个数的限制——对于这类题目,如果硬要 DP 那状态里必然要带记录一维选的数的个数,这就直接爆掉了。
考虑继续分析,设两个序列的最小最大值分别是 \(m_1,M_1,m_2,M_2\),那么题目的限制条件相当于 \(m_1+M_1=m_2+M_2\),并且还要求 \(M_1-m_1+M_2-m_2=k-2\),而第一个等式移个项就变成了 \(M_1-m_2=M_2-m_1\),故 \(M_1-m_2=\dfrac{k-2}{2}\)。而惊奇地我们发现,\(p\) 左边的部分的值域范围刚好是 \([m_1,M_2]\),\(q\) 右边的部分的值域范围刚好是 \([m_2,M_1]\)。也就是说左右两维实际上是独立的,只要满足 \(\dfrac{k-2}{2}\) 的限制就 ok 了。
于是思路就出来了,对这四种情况分别二分一下左右边界,然后倍增找到符合这种情况的上升和下降序列能够延申的上界和下界,然后看看上界和下界的差是否 \(\ge\dfrac{k-2}{2}\) 就行了。这样复杂度是 \(O(n\log^2n)\)。
const int MAXN=2e5;
const int LOG_N=18;
int n,k,a[MAXN+5];vector<int>occ[MAXN+5];
int getnxt(int x,int p){
int pos=upper_bound(occ[x].begin(),occ[x].end(),p)-occ[x].begin();
return occ[x][pos];
}
int getpre(int x,int p){
int pos=lower_bound(occ[x].begin(),occ[x].end(),p)-occ[x].begin()-1;
return occ[x][pos];
}
int Lup[LOG_N+2][MAXN+5],Ldw[LOG_N+2][MAXN+5],Rup[LOG_N+2][MAXN+5],Rdw[LOG_N+2][MAXN+5];
int get_lft_up(int x,int lim){for(int i=LOG_N;~i;i--)if(Lup[i][x]>=lim)x=Lup[i][x];return a[x];}
int get_lft_dw(int x,int lim){for(int i=LOG_N;~i;i--)if(Ldw[i][x]>=lim)x=Ldw[i][x];return a[x];}
int get_rit_up(int x,int lim){for(int i=LOG_N;~i;i--)if(Rup[i][x]<=lim)x=Rup[i][x];return a[x];}
int get_rit_dw(int x,int lim){for(int i=LOG_N;~i;i--)if(Rdw[i][x]<=lim)x=Rdw[i][x];return a[x];}
int main(){
scanf("%d%d",&n,&k);if(k&1)return puts("-1"),0;
for(int i=0;i<=n+1;i++)occ[i].pb(0);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),occ[a[i]].pb(i);
for(int i=0;i<=n+1;i++)occ[i].pb(n+1);
int res=n+1;
for(int i=1;i<=n;i++){
Lup[0][i]=getpre(a[i]+1,i);Ldw[0][i]=getpre(a[i]-1,i);
Rup[0][i]=getnxt(a[i]+1,i);Rdw[0][i]=getnxt(a[i]-1,i);
}
for(int i=1;i<=LOG_N;i++)for(int j=1;j<=n;j++){
Lup[i][j]=(Lup[i-1][j]==0)?0:Lup[i-1][Lup[i-1][j]];
Ldw[i][j]=(Ldw[i-1][j]==0)?0:Ldw[i-1][Ldw[i-1][j]];
Rup[i][j]=(Rup[i-1][j]==n+1)?(n+1):Rup[i-1][Rup[i-1][j]];
Rdw[i][j]=(Rdw[i-1][j]==n+1)?(n+1):Rdw[i-1][Rdw[i-1][j]];
}
for(int p=1;p<=n;p++){
int q=getnxt(a[p],p);if(q==n+1)continue;
int L,R,x,y;
//p:up, q:down, mid:up
x=y=-1;L=1,R=p;
while(L<=R){
int mid=L+R>>1;
int up=get_lft_up(p,mid),dw=get_lft_dw(p,mid);
if(up-dw>=k/2-1)x=mid,L=mid+1;
else R=mid-1;
}
L=q,R=n;
while(L<=R){
int mid=L+R>>1;
int up=get_rit_up(p,mid),dw=get_rit_dw(q,mid);
if(up-dw>=k/2-1)y=mid,R=mid-1;
else L=mid+1;
}
if(x!=-1&&y!=-1)chkmin(res,y-x);
//p:up, q:down, mid:down
x=y=-1;L=1,R=p;
while(L<=R){
int mid=L+R>>1;
int up=get_lft_up(q,mid),dw=get_lft_dw(p,mid);
if(up-dw>=k/2-1)x=mid,L=mid+1;
else R=mid-1;
}
L=q,R=n;
while(L<=R){
int mid=L+R>>1;
int up=get_rit_up(q,mid),dw=get_rit_dw(q,mid);
if(up-dw>=k/2-1)y=mid,R=mid-1;
else L=mid+1;
}
if(x!=-1&&y!=-1)chkmin(res,y-x);
//p:down, q:up, mid:up
x=y=-1;L=1,R=p;
while(L<=R){
int mid=L+R>>1;
int up=get_lft_up(p,mid),dw=get_lft_dw(q,mid);
if(up-dw>=k/2-1)x=mid,L=mid+1;
else R=mid-1;
}
L=q,R=n;
while(L<=R){
int mid=L+R>>1;
int up=get_rit_up(q,mid),dw=get_rit_dw(q,mid);
if(up-dw>=k/2-1)y=mid,R=mid-1;
else L=mid+1;
}
if(x!=-1&&y!=-1)chkmin(res,y-x);
//p:down, q:up, mid:down
x=y=-1;L=1,R=p;
while(L<=R){
int mid=L+R>>1;
int up=get_lft_up(p,mid),dw=get_lft_dw(p,mid);
if(up-dw>=k/2-1)x=mid,L=mid+1;
else R=mid-1;
}
L=q,R=n;
while(L<=R){
int mid=L+R>>1;
int up=get_rit_up(q,mid),dw=get_rit_dw(p,mid);
if(up-dw>=k/2-1)y=mid,R=mid-1;
else L=mid+1;
}
if(x!=-1&&y!=-1)chkmin(res,y-x);
}
if(res==n+1)puts("-1");
else printf("%d\n",res);
return 0;
}
标签:get,int,mid,up,Codeforces,Clownfish,序列,dw,Indefinite
From: https://www.cnblogs.com/tzcwk/p/18027842/Codeforces-1876F