# 2019CCPC哈尔滨 i题 Interesting Permutation 计数DP做法

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

#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;
}

}



©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客