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