2019CCPC Harbin I - Interesting Permutation

题目大意:

现有一个序列a,定义

\(f_i=MAX(a_1,a_2,...,a_i)\)

\(g_i=MIN(a_1,a_2,..,a_i)\)

\(h_i=f_i-g_i\)

$ 1\leq n\leq1e5$

解题思路:

首先判断一下h数组的合法性,若已判定h数组合法:

对于 \(h_i>h_{i-1},\)那么第\(i\)个位置只能作为当前的最大值或者最小值,答案乘以2,

否则,第\(i\)个位置可以选择放置最大值和最小值之间的可选择的数字,可以理解成插空,假设有cnt个可选择的数字,答案乘以cnt,放置之后cnt-=1;

同时对于 \(h_i>h_{i-1}\) 来说,会新产生 \(h_i-h_{i-1}-1\)的空位,将至加到cnt中即可。

代码

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=1e6+5;
const LL mod=1e9+7;
#define fi first
#define se second
#define ls (i<<1)
#define rs (i<<1|1)
#define mem(a,b) memset(a,b,sizeof(a))
#define eb emplace_back
#define mk make_pair
LL read()
{
    LL x=0,t=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){ if(ch=='-') t=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); }
    return x*t;
}
int h[N];
int main()
{
    int T=read();
    while(T--)
    {
        int n=read();
        for(int i=1;i<=n;i++) h[i]=read();
        LL ans=1;
        for(int i=1;i<=n;i++)
        {
            if(h[i]<h[i-1]||h[i]+1<i||h[i]>=n) ans=0;
        }
        if(!ans) {
            printf("0\n");
            continue;
        }
        int cnt=0;
        for(int i=2;i<=n;i++)
        {
            if(h[i]>h[i-1]) ans=2*ans%mod,cnt+=h[i]-h[i-1]-1;
            else ans=ans*(cnt--)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


对于排列计数的问题,可以考虑从第\(i\)个位置的关于\(1-i\) 的排列方案 转移到 第\(i+1\)个位置关于\(1-i+1\)的排列方案,比如这道2019ccpc哈尔滨的I题,假定初始\(a_1=1\),如果\(i->i+1,h_{i+1}>h_i\) ,那么当前第\(i+1\)个数就只能是最大或最小值,放置最大值就直接基于之前\(1-i\) 放置一个 \(Max+d\), \(d\)\(h_{i+1}-h_i\),或者放置最小值\(Min-d\),,然后将排列整体向右或向左平移,平移是一对一的映射,平移后方案数不变(如果一个排列放置\(Max+d\)\(Min-d\)越界,那么他的对称在放置\(Min-d\)\(Max+d\)时也会越界),因此不会影响计数。

posted @ 2021-05-13 17:33  DeepJay  阅读(111)  评论(0编辑  收藏  举报