2019 Multi-University Training Contest 2 :Everything Is Generated In Equal Probability(期望DP + 思维)

在这里插入图片描述
题目大意:输入一个 N,然后在[1,N]范围内随机得到一个数字n,然后再随机得到一个长度为n的排列,设这个排列有 c n t 1 cnt1 cnt1个逆序对,在这个排列的基础上,生成一个子序列,,这个子序列的逆序对个数为 c n t 2 cnt2 cnt2个…,然后再从这个子序列循环下去直到最初生成的排列为空,问 c n t 1 + c n t 2 + . . . + c n t n cnt1 + cnt2 +...+ cntn cnt1+cnt2+...+cntn的期望是多少

清华爷12分钟就A了,对于我来说感觉挺难想的吧…首先生成一个排列的概率要算,在生成的这个排列的上有多少个逆序对这不好想,但一个长度为 i i i排列最多有 i ∗ ( i − 1 ) 2 \frac{i * (i - 1)}{2} 2i(i1)个逆序对,最少有0个逆序对,然后计算生成的排列逆序对个数为0到 N ∗ ( N − 1 ) 2 \frac{N * (N - 1)}{2} 2N(N1)的排列的概率,这不现实,并且往下想会更加复杂。由于长度为 i i i 的 排列最多有 i ∗ ( i − 2 2 \frac{i * (i - 2}{2} 2i(i2对位置,而每一对位置是逆序对的概率是 1 2 \frac{1}{2} 21 ,因此生成一个长为 i i i排列的逆序对个数的期望是 i ∗ ( i − 1 ) 4 \frac{i * (i - 1)}{4} 4i(i1)。想到这感觉有了点思路,但接下来在这个排列的基础上生成的所有一个子序列的逆序对之和…由于生成一个长为 i i i的排列的概率是 1 N \frac{1}{N} N1,一个长为 i i i的序列最多有 1 2 i \frac{1}{2^i} 2i1,也就是说每一个子序列的概率为 1 2 i \frac{1}{2^i} 2i1,它所有的子序列的逆序对数应该是 0 到 i ∗ ( i − 1 ) 4 \frac{i * (i - 1)}{4} 4i(i1),但具体概率是多少不知道…好,就猜它是等概率的,然后一算答案不对,思路就断片了。

(情况细分下来的概率难以计算…我始终以为这是可以计算的只是我没找到方法,放大了这道题的难度,阻扰思维,而又不愿退一步。有些题需要你更深一步去想,有些题需要你从另一些角度看待而不是深钻一个死胡同,而我总是徘徊在两种思维之间,找不到正确的方法,需要锻炼的地方就是这吧)

update:我发现我一开始理解题意是错的…

题解:随机生成的长度为 i i i 的排列的期望逆序对数是 i ∗ ( i − 1 ) 4 \frac{i * (i - 1)}{4} 4i(i1) ,令DP[i] 为生成的序列长度为 i 的解,然后枚举长度 0 -> (i - 1) 转移即可。
在这里插入图片描述

(另外跟着标程走了一遍发现我自己的写法有很多可以优化的地方,这也是为什么常数老比别人大的原因吧…)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3;
typedef long long ll;
const int mod = 998244353;
ll dp[maxn + 10];
ll pw[maxn + 10];
ll c[maxn + 10][maxn + 10];
ll ans[maxn + 10];
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if(b & 1) r = r * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return r;
}
int main() {
	pw[0] = 1;
	for(int i = 0; i <= maxn; i++) c[i][0] = c[i][i] = 1;
	for(int i = 1; i <= maxn; i++) pw[i] = pw[i - 1] * 2 % mod;
	for(int i = 2; i <= maxn; i++)
		for(int j = 1; j <= i / 2; j++)
			c[i][j] = c[i][i - j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	dp[0] = dp[1] = 0;
	for(int i = 2; i <= maxn; i++) {
		dp[i] = (i * (i - 1) % mod) * pw[i - 2] % mod;
		for(int j = 0; j < i; j++)
			dp[i] = (dp[i] + c[i][j] * dp[j] % mod) % mod;
		dp[i] = dp[i] * fpow(pw[i] - 1,mod - 2) % mod;
	}
	ans[0] = 0;
	for(int i = 1; i <= maxn; i++) {
		ans[i] = 0;
		for(int j = 1; j <= i; j++)
			ans[i] = (ans[i] + dp[j]) % mod;
		ans[i] = ans[i] * fpow(i,mod - 2) % mod;
	}
	int n;
	while(~scanf("%d",&n))
		printf("%lld\n",ans[n]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值