CCPC-Wannafly Winter Camp Day4 G.置置置換/hdu4055 Number String(dp/波浪排列)

題目1

CCPC-Wannafly Winter Camp Day4 G.置置置換

wls有一個整數n(n<=1e4),他想請你算一下有多少1...n的排列(permutation)滿足:

對於所有的i (2≤i≤n) ,若i爲奇數,則a[i - 1]<a[i],否則a[i−1]>a[i]。

請輸出,答案mod 1e9 + 7。

思路來源

https://www.90yang.com/camp-day4-div2/ ①

https://www.icode9.com/content-4-133702.html ②

題解

①波浪排列:dp[i][j]表示,只考慮前i個數時,當前往序列裏填入第j大的數的方案數

一般博客將其視爲,dp[i][j]表示只考慮1-i的排列時,當前填入的數是j的方案數,

然後前面如果出現過大於等於j的數,就對其都進行加1,

個人認爲實際與這種插入的理解方法等效,確定每個位置在前綴中的rank,從而唯一確定排列

 

即上升時,從dp[i-1][1]到dp[i-1][j-1]轉化而來,

下降時,從dp[i-1][j]到dp[i-1][i-1]轉化而來,前i-1個數的rank j,因爲當前插入了rank j,從而往後挪了一位

暴力是O(n^3)的,爲了O(n^2)的複雜度,前綴和優化一下

 

②考慮f[i]表示1-i的波浪排列時的方案數,如1423,則將5的位置插入在4和2之間時,

將其劃分爲14和23兩個部分,其中需要將14反轉才能合法,41523,

而4132被劃分爲41和32兩個部分時,需要將32反轉才合法,也對應41523,

因此,f[i],枚舉插入位置時,將序列分爲C(i-1,x)*f[x]*f[i-1-x],

原合法方案ab和ba的反轉,rev(a) b與b rev(a)是一種方案,所以需要除以2

代碼1

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,mod=1e9+7;
int n,dp[N][N],sum[N][N];
int main(){
    scanf("%d",&n);
    dp[1][1]=sum[1][1]=1;
    for(int i=2;i<=n;++i){
        for(int j=1;j<=i;++j){
            if(i%2==0){
                dp[i][j]=(sum[i-1][i-1]-sum[i-1][j-1]+mod)%mod;//上一位置rank[j,i-1]
            }
            else{
                dp[i][j]=sum[i-1][j-1];//上一位置rank[1,j-1]
            }
            sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
        }
    }
    printf("%d\n",sum[n][n]);
    return 0;
}

代碼2

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,mod=1e9+7,inv2=(mod+1)/2;
int n,f[N],c[N][N];
int main(){
    c[1][0]=c[1][1]=1;
    for(int i=2;i<N;++i){
        c[i][0]=1;
        for(int j=1;j<=i;++j){
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
        }
    }
    scanf("%d",&n);
    f[0]=f[1]=1;
    for(int i=2;i<=n;++i){
        //考慮在前i-1個數中插入i 且前半段有j個
        for(int j=0;j<=i-1;++j){
            f[i]=(f[i]+1ll*c[i-1][j]*f[j]%mod*f[i-j-1]%mod)%mod;
        }
        f[i]=1ll*f[i]*inv2%mod;
    }
    printf("%d\n",f[n]);
    return 0;
}

題目2

hdu4055 Number String

給你一個字符串s(|s|<=1e3),僅由'>'、'<'和'?'構成,

計n=strlen(s),則串s代表了一個1到n+1的排列的大小關係

s[i] = 'D'表示排列中a[i] > a[i+1],s[i] = 'I'表示排列中a[i] < a[i+1],s[i]='?'表示不限制a[i]和a[i+1]的關係

比如排列 {3, 1, 2, 7, 4, 6, 5} 表示爲字符串 DIIDID,求滿足s串大小關係的合法排列的數量

思路來源

https://blog.csdn.net/jayye1994/article/details/12361481

題解

和上面那個題第一問一樣的,?時就從sum[i-1][i-1]轉移過來即可,不作限制

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,mod=1e9+7;
int n,dp[N][N],sum[N][N];
char s[N];
int main(){
    while(~scanf("%s",s+2)){
        dp[1][1]=sum[1][1]=1;
        n=strlen(s+2)+1;
        //printf("n:%d\n",n);
        for(int i=2;i<=n;++i){
            for(int j=1;j<=i;++j){
                if(s[i]=='D'){
                    dp[i][j]=(sum[i-1][i-1]-sum[i-1][j-1]+mod)%mod;
                }
                else if(s[i]=='I'){
                    dp[i][j]=sum[i-1][j-1];
                }
                else{
                    dp[i][j]=sum[i-1][i-1];
                }
                sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
            }
        }
        printf("%d\n",sum[n][n]);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章