求有多少个排列
p
1
,
p
2
,
.
.
.
,
p
n
{p1, p2, ..., pn}
p1,p2,...,pn 满足
p
1
=
s
,
p
n
=
t
p1 = s, pn = t
p1=s,pn=t 且对于
1
<
i
<
n
1 < i < n
1<i<n 满足$ pi − 1 < pi $且
p
i
+
1
<
p
i
p_{i + 1} < pi
pi+1<pi 或
p
i
−
1
>
p
i
p_{i − 1} > pi
pi−1>pi
且
p
i
+
1
>
p
i
p_{i + 1} > p_i
pi+1>pi。输出答案对 109 + 7 取模。
数据范围:
1
≤
s
,
t
≤
n
≤
2000
,
s
=
̸
t
1 ≤ s, t ≤ n ≤ 2000, s =\not t
1≤s,t≤n≤2000,s≠t。
令
f
(
i
,
j
,
x
)
f (i, j, x)
f(i,j,x) 表示从小到大前 i 个点已经填好了,构成了 j 条不包含
s
,
t
s, t
s,t
的前后都可以接上比端点大的数字的链,s 和 t 所在的链状态为 x。
那么加入一个新的(也是最大的)数字时可以让他单独一条链,还有就只能把两条链利用这个数字放在中间来合并。
AC Code:
#include<bits/stdc++.h>
#define maxn 2005
#define mod 1000000007
using namespace std;
int n,s,t,f[2][maxn];
int main(){
scanf("%d%d%d",&n,&s,&t);
int now = 1 , pre = 0 , i , j;
f[0][0] = 1;
for(i=1;i<n;i++,swap(now,pre)){
memset(f[now],0,sizeof f[now]);
for(j=0;j<=i;j++)
if(f[pre][j]){
if(i!=s && i!=t){
f[now][j+1] = (f[now][j+1] + f[pre][j]) % mod;
if(j)
f[now][j-1] = (f[now][j-1] + 1ll * f[pre][j] * (j * (j-1+(i>s)+(i>t)))) % mod;
}
else{
f[now][j] = (f[now][j] + f[pre][j]) % mod;
if(j)
f[now][j-1] = (f[now][j-1] + 1ll * f[pre][j] * j) % mod;
}
}
}
printf("%d\n",(f[pre][0] + mod) % mod);
}
类似的题:
ZJOI2012波浪(这个是基于插入的链合并DP)
换了插入的写法,慢了一倍??
AC Code:
#include<bits/stdc++.h>
#define maxn 2005
#define mod 1000000007
using namespace std;
int n,s,t,f[2][maxn];
int main(){
scanf("%d%d%d",&n,&s,&t);
int now = 1 , pre = 0;
f[0][0] = 1;
for(int i=1;i<n;i++,swap(now,pre)){
memset(f[now],0,sizeof f[now]);
for(int j=0;j<=i;j++)
if(f[pre][j]){
if(i!=s && i!=t){
f[now][j+1] = (f[now][j+1] + 1ll * f[pre][j] * (j+1)) % mod;
if(j)
f[now][j-1] = (f[now][j-1] + 1ll * f[pre][j] * (j-1+(i>s)+(i>t))) % mod;
}
else{
f[now][j] = (f[now][j] + f[pre][j]) % mod;
if(j)
f[now][j-1] = (f[now][j-1] + f[pre][j]) % mod;
}
}
}
printf("%d\n",(f[pre][0] + mod) % mod);
}