小C有一个合并排列机,它可以合并两个长度为n的排列A,B。
合并一共有2*n步,一开始答案的数列为空,每一步有两个选项:
(1).若A不为空,则你可以删掉A开头的元素,放到答案数列的末尾。
(2).若B不为空,则你可以删掉B开头的元素,放到答案数列的末尾。
以上两个选项每一步只能选一个
小C不喜欢重复的东西,定义两个排列的价值
f
(
A
,
B
)
f(A,B)
f(A,B)为他们合并可以生成的答案数列的种数
两个答案数列不同当且仅当某一位不同
现在小C想知道,随机选择两个长度为n的排列A,B。求
f
(
A
,
B
)
f(A,B)
f(A,B)期望
为了避免精度误差,你需要输出
a
n
s
∗
n
!
n
!
ans*n!n!
ans∗n!n!对p取模的值,显然这是个整数。
这个题有3条坑。
1:对于确定的A,B,有多少种操作序列并不代表有多少种答案数列,有重复。
发现重复只会出现在答案数列中有一段,满足条件C:
所有出现过的数字都有两份,并且可以分成两个没有重复数字的排列。
那么这两个排列对于AB的归属可以互换。
对于确定的排列A,B,我们设
f
i
,
j
f_{i,j}
fi,j表示已经处理完
A
的
前
i
个
和
B
的
前
j
个
A的前i个和B的前j个
A的前i个和B的前j个数后不同的答案数列数:
f
i
,
j
=
f
i
−
1
,
j
+
f
i
,
j
−
1
−
∑
d
=
1
g
(
i
−
d
+
1
,
i
,
j
−
d
+
1
,
j
)
∗
f
i
−
d
,
j
−
d
f_{i,j} = f_{i-1,j} + f_{i,j-1} - \sum_{d=1} g(i-d+1,i,j-d+1,j) * f_{i-d,j-d}
fi,j=fi−1,j+fi,j−1−∑d=1g(i−d+1,i,j−d+1,j)∗fi−d,j−d
其中
g
(
a
,
b
,
c
,
d
)
g(a,b,c,d)
g(a,b,c,d)表示由
A
a
−
b
A_{a-b}
Aa−b和
B
c
−
d
B_{c-d}
Bc−d中的字符按序可以组成多少个满足条件
C
C
C的数列。
这样可以把重复的全部剪掉。
2:发现答案并不是这样的,如果一个数列满足条件
C
C
C但是它的一个前缀也满足,那么去重的过程会多去重,去重过头了,那么
g
(
a
,
b
,
c
,
d
)
g(a,b,c,d)
g(a,b,c,d)表示由
A
a
−
b
A_{a-b}
Aa−b和
B
c
−
d
B_{c-d}
Bc−d中的字符按序可以组成多少个满足条件
C
C
C并且前缀都不满足条件
C
C
C的数列。(但是我们并不需要考虑如何算这个
g
g
g)
3:考虑对于不确定的
A
,
B
A,B
A,B,我们如何设计状态使得我们仍然可以得到
g
g
g数组。
现在的
g
g
g是
g
(
d
)
g(d)
g(d)表示长度为
d
d
d的满足条件
C
C
C并且前缀不满足条件
C
C
C的数列的数量,
这个可以
O
(
n
2
)
O(n^2)
O(n2)递推。
但是我们现在需要知道
A
,
B
A,B
A,B中有多少对相等的数。
设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示已经处理完
A
的
前
i
个
和
B
的
前
j
个
A的前i个和B的前j个
A的前i个和B的前j个数,有
k
k
k对相等的数。
这样就可以DP了,注意到我们要注意到
f
i
,
j
,
k
f_{i,j,k}
fi,j,k是在已经确定了
n
n
n种数字中
i
+
j
−
k
i+j-k
i+j−k种数字的意义下计算的,这样才能转移。
AC Code:
#include<bits/stdc++.h>
#define maxn 205
#define LL long long
using namespace std;
int mod;
int n,fac[maxn]={1,1},inv[maxn]={1,1},invf[maxn]={1,1};
int f[maxn][maxn][maxn],g[maxn][maxn],G[maxn];
int A(int a,int b){
if(a < 0 || b < 0 || a < b) return 0;
return fac[a] * 1ll * invf[a-b] %mod;
}
void add(int &a,LL b){
a = (1ll * a + b) % mod;
}
int main(){
freopen("R.in","r",stdin);
freopen("R.out","w",stdout);
scanf("%d%d",&n,&mod);
for(int i=2;i<=n;i++)
fac[i] = 1ll * fac[i-1] * i % mod,
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod;
g[0][0] = 1;
for(int i=1;i<=2*n;i++){
for(int j=(i/2)+1;j<=i;j++)
g[i][j] = (g[i-1][j-1] + g[i-1][j]) % mod;
if(i % 2 == 0) G[i/2] = (g[i-1][i/2] + g[i-1][i/2-1]) % mod;
}
f[0][0][0] = 1;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=min(i,j);k++)
if(i || j){
if(i) add(f[i][j][k] , 1ll * f[i-1][j][k] * (n-(i-1+j-k)) % mod);
if(j) add(f[i][j][k] , 1ll * f[i][j-1][k] * (n-(i+j-1-k)) % mod);
if(j && k) add(f[i][j][k] , 1ll * f[i][j-1][k-1] * (i-k+1) % mod);
if(i && k) add(f[i][j][k] , 1ll * f[i-1][j][k-1] * (j-k+1) % mod);
for(int p=1;p<=k;p++)
add(f[i][j][k] , - 1ll * A(n-(i-p+j-k),p) * G[p] % mod * f[i-p][j-p][k-p] % mod);
}
printf("%d\n",(f[n][n][n]+mod)%mod);
}