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