题目大意
给出一个 n n n 的排列 a i a_i ai,定义三个序列:
- f i = m a x { a 1 , a 2 , . . . , a i } f_i = max\{a_1, a_2, ... , a_i\} fi=max{ a1,a2,...,ai}
- g i = m i n { a 1 , a 2 , . . . , a i } g_i = min\{a_1, a_2, ... , a_i\} gi=min{ a1,a2,...,ai}
- h i = f i − g i h_i = f_i - g_i hi=fi−gi
现在给定 n n n 求有多少种排列满足给定的 h h h。
解题思路
首先应该注意到,序列 h h h 一定是单调不减的,而且如果一个数超过了 n − 1 n - 1 n−1 那么序列非法。
如何去计数?思路没有打开,一直局限于确定前两个数那样去计数,这样实际上是很麻烦且没办法用程序统计的。
- 若 h i − 1 < h i h_{i - 1} < h_i hi−1<hi,那么第 i i i 个位置一定是前缀的最值之一,这样才会扩大 h h h,那么总的方案数乘二。使用变量 s u m sum sum 记录最大值和最小值之间还有多少个数没填,该情况下会使可用的数变多, s u m = s u m + h i − h i − 1 − 1 sum = sum + h_i - h_{i-1} - 1 sum=sum+hi−hi−1−1。
- 若 h i − 1 < h i h_{i - 1} < h_i hi−1<hi,这时把记录的可用数其中之一填入,则总的方案数乘上 s u m sum sum,然后 s u m = s u m − 1 sum = sum - 1 sum=sum−1。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const int maxn = 2e5 + 10;
const int Mod = 1e9 + 7;
int a[maxn];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T, n;
cin >> T;
while(T--) {
cin >> n;
bool ok = 1;
for(int i = 1; i <= n; i++) {
cin >> a[i];
if(a[i - 1] > a[i] || a[i] >= n) ok = 0;
}
if(!ok) {
cout << 0 << ENDL;
continue;
}
ll ans = 1, sum = 0;
for(int i = 2; i <= n; i++) {
if(a[i] > a[i - 1]) {
ans = ans * 2 % Mod;
sum += a[i] - a[i - 1] - 1;
} else {
ans = ans * sum % Mod;
sum--;
}
}
cout << ans << ENDL;
}
return 0;
}