【概率dp·数学期望·打表】Everything Is Generated In Equal Probability (HDU2019多校第二场)

题目传送门

题目还是非常有意思的
首先,由于这个排列非常的随机,取子序列的操作也很随机,所以答案应该与排列的长度有关

先不考虑递归,我们试着求一下长度为 n n n的随机排列的逆序对的个数的期望
有两种思路(目前:

1.先确定排列的第一位 逆序对可以是由第一个数和后面的数产生的,也可以是后面的数自己产生的
后者是一个递归问题
所以我们只需要考虑第一种情况 然后加上长度为 1 1 1~ n − 1 n-1 n1的随机排列的逆序对的个数的期望
对于第一种情况,显然逆序对的个数与第一个数的值有关 不妨设第一个数的值为 a a a,逆序对个数为 s s s
a = 1 a=1 a=1时, s = n − 1 s=n-1 s=n1
a = 2 a=2 a=2时, s = n − 2 s=n-2 s=n2
a = 3 a=3 a=3时, s = n − 3 s=n-3 s=n3
···
a = n − 1 a=n-1 a=n1时, 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++nn1=n2n(n1)=2n1

再算上上文提到的第二种情况
所以长度为 i i i的随机排列的逆序对的个数的期望 f [ i ] = ∑ j = 1 i j − 1 2 f[i]=\sum_{j=1}^{i} \frac{j-1}{2} f[i]=j=1i2j1

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 i1
删2位 有 c [ i ] [ 2 ] c[i][2] c[i][2]种 剩下长度为 i − 2 i-2 i2
删3位 有 c [ i ] [ 3 ] c[i][3] c[i][3]种 剩下长度为 i − 3 i-3 i3
···
删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[i1]+2ic[i][2]dp[i2]++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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值