[2019CCPC哈尔滨] I Interesting Permutation 组合数学

对于一个序列,定义 f i f_{i} fi表示它的前缀最大值, g i g_{i} gi表示它的前缀最小值, h i = f i − g i h_{i}=f_{i}-g_{i} hi=figi。现在给出一个 h h h数组,求问合法的原序列的个数。
注意到 h i h_{i} hi是单调不降的,并且 h 1 = 0 h_{1}=0 h1=0 h n ≤ n − 1 h_{n}\leq n-1 hnn1。首先先判断对于 h i h_{i} hi都是合法的,若不合法则为 0 0 0
否则考虑若 h i > h i − 1 h_{i} >h_{i-1} hi>hi1,说明 i i i这一位一定是取到了最大值或者是最小值,答案是原先的答案 2 2 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,则一定是往中间的空位插入。
对于排列的这种问题,可以考虑增量转移。

#include
using namespace std;
const int N=1e5+7;
const int mod=1e9+7;
typedef long long ll;
int a[N];
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        int n;
        ll ans=1;
        scanf("%d",&n);
        bool ok=1;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        if(a[1]!=0) ok=0;
        for(int i=2;i<=n;i++) {
            if(a[i]<a[i-1]) ok=0;
            if(a[i]>=n) ok=0;
        }
        if(!ok) puts("0");
        else {
            int cnt=0;
            for(int i=2;i<=n;i++) {
                if(a[i]>a[i-1]) ans=(2*ans)%mod,cnt+=a[i]-a[i-1]-1,cnt%=mod;
                if(a[i]==a[i-1]) ans=(ans*cnt)%mod,cnt=(cnt+mod-1)%mod;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(比赛题解)