[cf1761F]Anti-median
-
考虑长度为\(3\)的区间,即\(\forall i\in [2,n),a_{i}<a_{i-1},a_{i+1}\)或\(a_{i}>a_{i-1},a_{i+1}\)
不妨假设\(a_{1}<a_{2}\),则其余部分即形如\(a_{1}<a_{2}>a_{3}<...a_{n}\)
-
考虑长度为\(5\)的区间,即\(\forall i\in [3,n-2],\begin{cases}a_{i}<a_{i-2}或a_{i}<a_{i+2} & 2\not\mid i\\a_{i}>a_{i-2}或a_{i}>a_{i+2}&2\mid i\end{cases}\)
对于子序列\(\{a_{1},a_{3},...,a_{n/n-1}\}\),其中不存在"极大值",即在最小值两侧单调递增
类似的,子序列\(\{a_{2},a_{4},...,a_{n/n-1}\}\)在最大值两侧单调递减
事实上,上述条件已经充分必要——
考虑任意长度的区间\([l,r]\),记假设区间中点\(k\)
不妨假设\(2\not\mid k\)且\(k\)在\(\{a_{1},a_{3},...,a_{n/n-1}\}\)中最小值的左侧
结合前者性质,有\(\forall i\in [l,k)\cup \{k+1\},a_{i}>a_{k}\),即\(a_{k}\)不为中位数
将序列重排为\(\{b_{n}\}=\{a_{1},a_{3},...,a_{n/n-1},a_{n-1/n},a_{n-3/n-2},...,a_{2}\}\),并将首尾相接
设\(b_{i}=1,b_{j}=n\),则将\(\{b_{n}\}\)在\(j\)和\(j+1\)中间断开后,其在\(i\)两侧单调递增
枚举\(i\le \lceil\frac{n}{2}\rceil\),即向左右共拓展\(n-2\)次,且任意时刻区间\([l,r]\)满足\(\begin{cases}l+r\le n+1&l\le r\\l+r>n+1&l>r\end{cases}\)
-
关于限制,当拓展到该位置时,当前的区间仅有两种,分别记录方案数即可
-
关于两个区间之间的转移,左右拓展次数均确定,而限制即任意时刻\(左-右\in [x,y]\)
另外,两个限制不会同时不满足,并用经典做法减掉对应方案数即可
具体的,设左右拓展次数为\(l,r\),则答案为\(\begin{cases}0&l-r\not\in [x,y]\\{l+r\choose l}-{l+r\choose x-1+r}-{l+r\choose y+1+r}&l-r\in [x,y]\end{cases}\)
初始状态枚举\(i\)计算,最终状态枚举\(j\)求和,但当不存在限制时两者会同时枚举
此时,答案即\(\sum_{i=1}^{\lceil\frac{n}{2}\rceil}\sum_{r=\lceil\frac{n}{2}\rceil-i}^{n-i-1}{n-2\choose r}-{n-2\choose 2i-n-2+r}-{n-2\choose 2i-1+r}\),预处理\({n-2\choose m}\)的前缀和即可
时间复杂度为\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000005,mod=1000000007;
int t,n,m,ans,a[N],fac[N],inv[N],pos[N];
int l1[N],r1[N],l2[N],r2[N],f1[N],f2[N],sum[N];
int add(int x,int y){
x+=y;
return (x<mod ? x : x-mod);
}
int C(int n,int m){
if ((m<0)||(m>n))return 0;
return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int get_sum(int l,int r){
l=max(l,0),r=min(r,n-2);
if (l>r)return 0;
return add(sum[r],(l ? mod-sum[l-1] : 0));
}
bool check(int l,int r){
if (l<=r)return l+r<=n+1;
return l+r>n+1;
}
int calc(int l,int r,int x,int y){
return add(add(C(l+r,l),mod-C(l+r,x-1+r)),mod-C(l+r,y+1+r));
}
int move(int l1,int r1,int l2,int r2){
int l=(l1-l2+n)%n,r=(r2-r1+n)%n;
if ((l+r>=n)||(!check(l2,r2)))return 0;
if (l1<=r1)return calc(l,r,l1+r1-n-1,l1+r1-2);
return calc(l,r,-n,l1+r1-n-2);
}
int work(){
m=0;
for(int i=1;i<=n;i++)pos[i]=0;
for(int i=1;i<=n;i++)
if (a[i]>0)pos[a[i]]=((i&1) ? (i+1>>1) : n-(i>>1)+1);
for(int i=1;i<=n;i++)
if (pos[i]){
if (i==1)m++,l1[m]=r1[m]=l2[m]=r2[m]=pos[i],f1[m]=1,f2[m]=0;
else{
l1[++m]=pos[i]%n+1,r1[m]=(pos[i]+i-2)%n+1;
l2[m]=(pos[i]+n-i)%n+1,r2[m]=(pos[i]+n-2)%n+1;
f1[m]=f2[m]=0;
if (m==1){
for(int j=1;j<=(n+1>>1);j++){
f1[m]=add(f1[m],move(j,j,l1[m],r1[m]));
f2[m]=add(f2[m],move(j,j,l2[m],r2[m]));
}
}
else{
f1[m]=(ll)f1[m-1]*move(l1[m-1],r1[m-1],l1[m],r1[m])%mod;
f1[m]=(f1[m]+(ll)f2[m-1]*move(l2[m-1],r2[m-1],l1[m],r1[m]))%mod;
f2[m]=(ll)f1[m-1]*move(l1[m-1],r1[m-1],l2[m],r2[m])%mod;
f2[m]=(f2[m]+(ll)f2[m-1]*move(l2[m-1],r2[m-1],l2[m],r2[m]))%mod;
}
l1[m]=r2[m]=pos[i];
}
if (i<n){
if (!check(l1[m],r1[m]))f1[m]=0;
if (!check(l2[m],r2[m]))f2[m]=0;
}
}
if (pos[n])return f1[m];
if (m){
int ans=0;
for(int i=(n+1>>1)+1;i<=n;i++){
ans=(ans+(ll)f1[m]*move(l1[m],r1[m],i%n+1,(i+n-2)%n+1))%mod;
ans=(ans+(ll)f2[m]*move(l2[m],r2[m],i%n+1,(i+n-2)%n+1))%mod;
}
return ans;
}
int ans=0;sum[0]=1;
for(int i=1;i<=n-2;i++)sum[i]=add(sum[i-1],C(n-2,i));
for(int i=1;i<=(n+1>>1);i++){
ans=add(ans,get_sum((n+1>>1)-i,n-i-1));
ans=add(ans,mod-get_sum(i-(n>>1)-2,i-3));
ans=add(ans,mod-get_sum(i+(n+1>>1)-1,i+n-2));
}
return ans;
}
int main(){
fac[0]=inv[0]=inv[1]=1;
for(int i=1;i<N;i++)fac[i]=(ll)fac[i-1]*i%mod;
for(int i=2;i<N;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<N;i++)inv[i]=(ll)inv[i-1]*inv[i]%mod;
scanf("%d",&t);
while (t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
ans=work();
for(int i=1;i<=n;i++)
if (a[i]>0)a[i]=n-a[i]+1;
printf("%d\n",add(ans,work()));
}
return 0;
}