题意:定义fi为前i个数的最大值,gi为前i个数的最小值,hi = fi - gi
现在告诉你每个位置的hi,问有多少种1~n的排列满足这些hi。
首先理性分析一波,第i位置的点必须要小于n,并且大于等于i-1,还要大于等于上一个数字。
赛时的我分了三种情况讨论,当前已经选了上边界的点,即n点,当前已经选了下边界的点,即1,还有当前并没有选择边界点。并将选了前i个数字的三种情况的方案数分别记为dp[i][0],dp[i][1],dp[i][2]。。赛后发现好像dp[i][0]永远等于dp[i][1]啊。。两种就可以了。
那么只需要讨论当前位置的h[i]和上一个h[i]是相等还是大于就可以判断出转移的方程式了。
h[i] == h[i-1]:假如当前已选数字中最大值和最小值的差值为h[i],且之前选了i个数字,那么接下来能选的数字就有(h[i] - i + 2)种情况,所以直接dp[i][j] *= (h[i] - i + 2)即可
h[i] > h[i-1]:先考虑未选择边界点时的情况。假设已选的序列的最小值为a,由于最小值和最大值都不为边界点,则可以得到
a > 1 && a + h[i-1] < n
能够算出有多少种a是符合的条件的。假设有Q种a是符合上述式子的,则每一种a所对应的的方案数是相同的。(注释1)所以可以得到每一种最小值的方案数,即dp[i-1][2] / Q, 再算出当最大值最小值之差变为h[i]时又多少种a是符合的(设为k),则可以推出dp[i][2] = dp[i-1][2] / Q * k,其中除法要预处理逆元。
注释1的证明:假设对于两个符合上述式子的最小值a,b。以对于任意一个以a为最小值的序列,每个元素都加上(b-a),则可以得到一个以b为最小值的序列。因为a,b都是满足上式的,加了(b-a)也不会使得序列不合法
然后再去考虑此时从dp[i-1][2]转移到dp[i][0] dp[i][1]的情况,发现只会有一种合法的a转移到边界。即a = n - h[i],当然,我们无需知道a的具体值,只需要知道有一种就行了。所以dp[i][0] += dp[i-1][2] / Q + dp[i-1][0]. dp[i][1]同理
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define IO ios::sync_with_stdio(false)
#define rep(i,a,n) for (int i = a; i <= n; i++)
ll pow_mod(ll a,ll b,ll c=mod,ll ans=1){while(b){if(b&1) ans=(a*ans)%c;a=(a*a)%c,b>>=1;}return ans;}
ll inv_mod(ll a){return pow_mod(a,mod-2);}
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;
ll dp[maxn][3], inv[maxn], co[maxn];
int main(){
IO;
int n, m, t;
rep(i,0,1e5)
inv[i] = inv_mod(i);
cin >> t;
while(t--){
cin >> n;
rep(i,1,n){
cin >> co[i];
rep(j,0,2)
dp[i][j] = 1;
}
if(co[i] != 0){
cout << 0 << endl;
continue;
}
dp[1][0] = dp[1][1] = 1; dp[1][2] = n - 2;
bool flag = 1;
for(int i = 2; i <= n && flag; i++){
if(co[i] < i - 1 || co[i] >= n || co[i] < co[i-1])
flag = 0;
else if(co[i] == co[i-1]){
rep(j, 0, 2)
dp[i][j] = (dp[i-1][j] * (co[i] - i + 2)) % mod;
}else{
if(co[i] != n - 1){
int minn = max(0, n-2-co[i-1]);
if(co[i] == n-1)
minn = 0;
dp[i][0] = (dp[i-1][0] + (dp[i-1][2] * inv[minn])) % mod;
dp[i][1] = (dp[i-1][1] + (dp[i-1][2] * inv[minn])) % mod;
dp[i][2] = (dp[i-1][2] * inv[minn] % mod * (max(0, n-2-co[i]) % mod * 2)) % mod;
}
}
}
if(!flag)
cout << 0 << endl;
else
cout << (dp[n][0] + dp[n][1]) % mod << endl;
}
}