Interesting Permutation(思维)

Interesting Permutation

[Link](Problem - I - Codeforces)

题意

给你长度为n的数列 h n h_n hn,这个数列的某一项 h i h_i hi是由某个原数列前 i i i项的最大值减去最小值的的结果,问你用 1 − n 1-n 1n这些数字可以构造出多少个原数列 a n a_n an

题解

​ 首先发现 h 1 h_1 h1一定为零,从前往后看因为每次加入一个新的 a i a_i ai就会产生三种情况1. 使得最大值变大 2. 使得最小值变小 3.介于原来最大最小值之间,最大最小不变,因此发现 h n h_n hn一定是一个非严格递增数列,并且 h m a x = = n − 1 h_{max}==n-1 hmax==n1。所以先排除不合法的情况,当合法以后我们考虑怎么去推这个合法原数列的方案数。

​ 如果按照每一位可以放哪些数这样去思考是很困难的,因为只问你合法的方案数是多少,所以可以考虑用一种增量的方法来统计答案,假设第一个数为 x x x我们先不考虑 x x x的具体值只考虑一个相对大小,然后依次考虑 h 2 , h 3 , . . . , h n h_2,h_3,...,h_n h2,h3,...,hn,如果 h i > h i − 1 h_i>h_{i-1} hi>hi1就说明 a i a_i ai可以是前 i i i个数里的最大值也可以是最小值,所以对答案的贡献就是乘2,相当于把一个可数轴上的区间向左或向右延申了,因此新增中间 h i − h i − 1 − 1 h_i-h_{i-1}-1 hihi11个空位没有填,如果 h i = = h i − 1 h_i==h_{i-1} hi==hi1就说明 a i a_i ai是介于最大最小值的这个区间里可填的数的一个,假设有 l e n len len个可选的,那么对答案的贡献就是乘len,然后因为选了一个所以len–。当这样把原数组的方案数推出来以后,发现每个合法的方案都确定了一个最开始的 x x x,因此可以不重不漏的统计答案。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1);
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n; bool ok = false;
        for (int i = 0; i < n; i ++ ) {
            cin >> a[i];
            if (a[i] >= n || (i && a[i] == 0) || (!i && a[i]) || (i && a[i] < a[i - 1])) ok = true;
        }
        if (ok) {
            cout << 0 << endl;
            continue ;
        }
        LL res = 1, len = 0;
        for (int i = 1; i < n; i ++ ) {
            if (a[i] == a[i - 1]) res = (res * len) % mod, len --;
            else {
                res = (res * 2) % mod;
                len += (a[i] - a[i - 1] - 1) % mod; 
            }
        }
        cout << res << endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值