2019 CCPC 哈尔滨 I. Interesting Permutation(思维+组合计数)

传送门


题目大意

给出一个 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=figi

现在给定 n n n 求有多少种排列满足给定的 h h h

解题思路

首先应该注意到,序列 h h h 一定是单调不减的,而且如果一个数超过了 n − 1 n - 1 n1 那么序列非法。

如何去计数?思路没有打开,一直局限于确定前两个数那样去计数,这样实际上是很麻烦且没办法用程序统计的。

  • h i − 1 < h i h_{i - 1} < h_i hi1<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+hihi11
  • h i − 1 < h i h_{i - 1} < h_i hi1<hi,这时把记录的可用数其中之一填入,则总的方案数乘上 s u m sum sum,然后 s u m = s u m − 1 sum = sum - 1 sum=sum1
#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;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/121466710
今日推荐