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;
}

 

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