@Explodingkonjac 学长讲的做法,题解区有一篇讲这个的,但是感觉细节真的好多……
我们先尝试构造出来一个合法序列。怎么构造呢?先枚举 ,然后先将序列 设为 个 后面接 个 ,也就是先到最大值再到最终值。
然后考虑往这个数列中插入若干 这个序列。画到一个折线图上,发现这就相当于增加一块凹陷。不难发现,对于一个有解的 ,这样一定能构造出解。
设新前缀和序列为 同时,在一个 的位置后面插入一个 能使 在 的出现次数增加 。于是在构造的同时就可以计数了!
具体地,从大往小考虑 的出现次数。设 在 中出现 次, 中 次,则要通过插入 的操作将 加到 。反过来,一共要将 个 分到 个集合中,插板法,贡献为 。然后 会加上 ,继续往下推即可。
细节包括但不限于:
-
要考虑一开始的 否则一开始下降的情况统计不到。
-
的情况要注意一下。
-
等等。
code:
int n,m,fac[N],ifac[N],c[N],b[N];
il int Mod(int x,int y){return x+y>=mod?x+y-mod:x+y;}
il int binom(int x,int y){
if(x<0||y<0||x<y)return 0;
return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
il int qpow(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod,y>>=1;
}
return ret;
}
void Yorushika(){
scanf("%d",&n);
int mx=0;
rep(i,0,n+n)c[i]=0;
c[n]=1;
rep(i,1,n){
int x;scanf("%d",&x);
c[x+n]++,mx=max(mx,x+n);
}
fac[0]=1;
rep(i,1,n)fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=qpow(fac[n],mod-2);
drep(i,n-1,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
int ans=0;
rep(j,0,n+n){
if(!c[j])continue;
int x=n,res=1;
rep(i,0,n+n)b[i]=0;
b[n]=1;
rep(i,1,mx-n)b[++x]++;
rep(i,1,max(mx,n)-j)b[--x]++;
bool fl=1;
if(!fl)continue;
drep(i,n+n,0){
if(!c[i]){if(b[i]){res=0;break;}continue;}
res=1ll*res*binom(c[i]-1,b[i]-1)%mod;
if(i)b[i-1]+=c[i]-b[i];
}
ans=Mod(ans,res);
}
printf("%d\n",ans);
}
signed main(){
int t=1;
scanf("%d",&t);
while(t--)
Yorushika();
}