【概率dp·数学期望·打表】Everything Is Generated In Equal Probability (HDU2019多校第二场)
题目还是非常有意思的
首先,由于这个排列非常的随机,取子序列的操作也很随机,所以答案应该与排列的长度有关
先不考虑递归,我们试着求一下长度为
n
n
n的随机排列的逆序对的个数的期望
有两种思路(目前:
1.先确定排列的第一位 逆序对可以是由第一个数和后面的数产生的,也可以是后面的数自己产生的
后者是一个递归问题
所以我们只需要考虑第一种情况 然后加上长度为
1
1
1~
n
−
1
n-1
n−1的随机排列的逆序对的个数的期望
对于第一种情况,显然逆序对的个数与第一个数的值有关 不妨设第一个数的值为
a
a
a,逆序对个数为
s
s
s
a
=
1
a=1
a=1时,
s
=
n
−
1
s=n-1
s=n−1
a
=
2
a=2
a=2时,
s
=
n
−
2
s=n-2
s=n−2
a
=
3
a=3
a=3时,
s
=
n
−
3
s=n-3
s=n−3
···
a
=
n
−
1
a=n-1
a=n−1时,
s
=
1
s=1
s=1
a
=
n
a=n
a=n时,
s
=
0
s=0
s=0
而这每一种情况是等概率的,都是
1
/
n
1/n
1/n
所以他们的期望就是
0
n
+
1
n
+
2
n
+
⋅
⋅
⋅
+
n
−
1
n
=
n
(
n
−
1
)
2
n
=
n
−
1
2
\frac{0}{n}+\frac{1}{n}+\frac{2}{n}+···+\frac{n-1}{n} =\frac{\frac{n(n-1)}{2}}{n}=\frac{n-1}{2}
n0+n1+n2+⋅⋅⋅+nn−1=n2n(n−1)=2n−1
再算上上文提到的第二种情况
所以长度为
i
i
i的随机排列的逆序对的个数的期望
f
[
i
]
=
∑
j
=
1
i
j
−
1
2
f[i]=\sum_{j=1}^{i} \frac{j-1}{2}
f[i]=∑j=1i2j−1
2.考虑每一对逆序对的产生:在
n
n
n个位置里面选两个,如果其中前面的数大于后面的数,就有了一对逆序对。
而前面的数大于后面的数的概率为
1
2
\frac{1}{2}
21
所以长度为
i
i
i的随机排列的逆序对的个数的期望
f
[
i
]
=
c
[
n
]
[
2
]
∗
1
2
f[i]=c[n][2]*\frac{1}{2}
f[i]=c[n][2]∗21
此外,还可以打表找规律:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<ctime>
using namespace std;
#define MAXN
#define MAXM 3005
#define LL long long
#define INF 0x3f3f3f3f
#define MOD 998244353
int num[100]={1,2,3,4,5,6,7,8,9,10,11,12};
int n=7;//调n
LL cnt=0,ans=0;
int main()
{
do
{
ans++;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(num[j]<num[i])
cnt=(cnt+1)%MOD;
}
while(next_permutation(num,num+n));
printf("%lf\n",1.0*cnt/ans);
return 0;
}
接下来考虑随机选择子序列递归的情况
下面开始概率dp
d
p
[
i
]
dp[i]
dp[i]表示长度恰好为
i
i
i的序列运行这个程序返回值的期望
题目本身给了我们一个不错的思考角度:
In mathematics, a subsequence is a sequence that can be derived from
another sequence by deleting some or no elements without changing the
order of the remaining elements. Note that empty subsequence is also a
subsequence of original sequence.
所以我们可以考虑删除
一共有
2
i
2^i
2i种删法
删0位 有
c
[
i
]
[
0
]
c[i][0]
c[i][0]种 剩下长度为
i
i
i
删1位 有
c
[
i
]
[
1
]
c[i][1]
c[i][1]种 剩下长度为
i
−
1
i-1
i−1
删2位 有
c
[
i
]
[
2
]
c[i][2]
c[i][2]种 剩下长度为
i
−
2
i-2
i−2
删3位 有
c
[
i
]
[
3
]
c[i][3]
c[i][3]种 剩下长度为
i
−
3
i-3
i−3
···
删i位 有
c
[
i
]
[
i
]
c[i][i]
c[i][i]种 剩下长度为
0
0
0
所以状态转移就是
d
p
[
i
]
=
f
[
i
]
+
c
[
i
]
[
0
]
2
i
∗
d
p
[
i
]
+
c
[
i
]
[
1
]
2
i
∗
d
p
[
i
−
1
]
+
c
[
i
]
[
2
]
2
i
∗
d
p
[
i
−
2
]
+
⋅
⋅
⋅
+
c
[
i
]
[
i
]
2
i
∗
d
p
[
0
]
\large dp[i]=f[i]+\frac{c[i][0]}{2^i}*dp[i]+\frac{c[i][1]}{2^i}*dp[i-1]+\frac{c[i][2]}{2^i}*dp[i-2]+···+\frac{c[i][i]}{2^i}*dp[0]
dp[i]=f[i]+2ic[i][0]∗dp[i]+2ic[i][1]∗dp[i−1]+2ic[i][2]∗dp[i−2]+⋅⋅⋅+2ic[i][i]∗dp[0]
把
c
[
i
]
[
0
]
2
i
∗
d
p
[
i
]
\frac{c[i][0]}{2^i}*dp[i]
2ic[i][0]∗dp[i]这一项移过去,就可以算出
d
p
[
i
]
dp[i]
dp[i]的值了
但是现在还没完
输入
N
N
N之后 这个
N
N
N不是直接就是序列的长度
还要在1到
N
N
N之间随机选一个
n
n
n 作为序列的长度(真随机
同样还是等概率的
所以答案应该是
∑
i
=
1
N
d
p
[
i
]
n
\sum_{i=1}^{N} \frac{dp[i]}{n}
∑i=1Nndp[i]
在程序中为了方便 这里用前缀和处理 最后再除以
N
N
N
最后要记得 题目有取模 所有的除法都要用逆元
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define MAXN 3005
#define MAXM 3005
#define LL long long
#define INF 0x3f3f3f3f
#define MOD 998244353
LL c[MAXN][MAXN],p[MAXN];
LL f[MAXN],dp[MAXN];
LL Pow(LL a,int b)
{
LL ans=1;
while(b)
{
if(b&1)
ans=ans*a%MOD;
a=a*a%MOD;
b>>=1;
}
return ans;
}
void C()
{
for(int i=0;i<=3000;i++)
for(int j=0;j<=i;j++)
{
if(j==0||i==j)
c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
}
return ;
}
void Init()
{
C();
p[0]=1,p[1]=2;
for(int i=2;i<MAXN;i++)
p[i]=p[i-1]*2%MOD;
for(int i=0;i<MAXN;i++)
p[i]=Pow(p[i],MOD-2);
for(int i=1;i<MAXN;i++)
f[i]=(f[i-1]+p[1]*(i-1)%MOD)%MOD;
for(int i=1;i<MAXN;i++)
{
LL tmp=0;
for(int j=1;j<=i;j++)
tmp=(tmp+c[i][j]*p[i]%MOD*dp[i-j]%MOD)%MOD;
tmp=(tmp+f[i])%MOD;
LL a=(1+MOD-p[i])%MOD;
dp[i]=tmp*Pow(a,MOD-2)%MOD;
}
for(int i=1;i<MAXN;i++)
dp[i]=(dp[i]+dp[i-1])%MOD;
}
int main()
{
Init();
int n;
while(~scanf("%d",&n))
{
printf("%lld\n",dp[n]*Pow(n,MOD-2)%MOD);
}
return 0;
}