NOIP模拟赛20190914 T3 空之轨迹【概率DP】

题目描述:

简化版题意:
随机生成一个 m + 1 m+1 m+1 个数的数列,第一个数为 0 0 0, 生成第 i i i 个数时,在前 i − 1 i − 1 i1 个数中等概率选择一个数 k k k, 则第 i i i 个数为 k + 1 k + 1 k+1。数字 i i i 有一个对应的权值 a i a_i ai,求数列权值和的期望。
m ≤ 21 m\le21 m21 m o d    998244353 \mod 998244353 mod998244353

题目分析:

(我所知的)大概有两种方法。

法一:排序差分

如果直接算期望转移的话,就要想办法表达已经选的前 i i i个数是那些数。

显然数的顺序是不重要的,我们只需要知道每种数有多少。将所得的 i i i个数拿来排序后,可以发现相邻两个数的差只能是0或1,所以就可以用一个 2 m 2^m 2m的状态来表示已经有的数。
f [ i ] f[i] f[i]表示已有数字状态为 i i i,还能再得到的权值和的期望,答案就是 f [ 0 ] f[0] f[0]
可以发现我们的转移每次都是涉及到状态中的所有数,所以不必将当前拿的哪一个数放进状态中。
只需枚举当前选的数字 j j j,记 i i i中数的个数为 c n t i cnt_i cnti,则 f [ i ] + = f [ i   a d d   j ] c n t i f[i]+=\frac {f[i~add~j]}{cnt_i} f[i]+=cntif[i add j]
复杂度 O ( m ⋅ 2 m ) O(m\cdot2^m) O(m2m)

Code:

#include<cstdio>
#define maxn 25
using namespace std;
const int mod = 998244353;
int n,m,a[maxn],f[1<<21],lg[1<<21],inv[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++) scanf("%d",&a[i]);
	inv[0]=inv[1]=1;
	for(int i=2;i<=m;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<1<<(m-1);i++) lg[i]=lg[i>>1]+1;
	for(int s=(1<<(m-1))-1;s>=1;s--){//begin shade 01, end have a 1 as barrier.
		f[s]=f[s<<1]+a[0];//origin 0->1
		for(int t=s,now,pre=-1,low,j=1;t;pre=now,j++){
			now=lg[low=t&-t], t^=low;
			if(t) f[s]=(f[s]+1ll*(f[t<<1|(s^t)]+a[j])*(now-pre))%mod;
			else f[s]=(f[s]+1ll*(f[low<<1|s]+a[j])*(now-pre))%mod;
		}
		f[s]=1ll*f[s]*inv[lg[s]+2]%mod;
	}
	printf("%d\n",(f[1]+a[0])%mod);
}

法二:期望的线性性(分开算每个点的概率*权值再累加)

最后的序列是一个长度为 m + 1 m+1 m+1的序列,设其权值和为 S S S
S = ∑ i = 1 m + 1 X i S=\sum_{i=1}^{m+1}X_i S=i=1m+1Xi X i X_i Xi为第 i i i个位置的权值
那么 E ( S ) = ∑ i = 1 m + 1 E ( X i ) E(S)=\sum_{i=1}^{m+1}E(X_i) E(S)=i=1m+1E(Xi)
E ( X i ) = ∑ j = 1 i − 1 p [ i ] [ j ] ∗ a [ j ] E(X_i)=\sum_{j=1}^{i-1}p[i][j]*a[j] E(Xi)=j=1i1p[i][j]a[j] p [ i ] [ j ] p[i][j] p[i][j]为第 i i i个数为 j j j的概率。

于是只需要求出 p [ i ] [ j ] p[i][j] p[i][j]。要算这个,我们就需要知道前 i − 1 i-1 i1个数中选了多少个 j − 1 j-1 j1,对应就会生成多少个 j j j p [ i ] [ j ] = E ( 生 成 的 j 的 个 数 ) i − 1 p[i][j]=\frac {E(生成的j的个数)}{i-1} p[i][j]=i1E(j),同样分开统计每个位置的贡献,分子的 E = ∑ k = 1 i − 1 p [ k ] [ j − 1 ] E=\sum_{k=1}^{i-1}p[k][j-1] E=k=1i1p[k][j1]

所以就是 p [ i ] [ j ] = 1 i − 1 ∗ ∑ k = 1 i − 1 p [ k ] [ j − 1 ] p[i][j]=\frac 1{i-1}*\sum_{k=1}^{i-1}p[k][j-1] p[i][j]=i11k=1i1p[k][j1],用前缀和优化一下就可以做到 O ( m 2 ) O(m^2) O(m2)

Code:

#include<cstdio>
#define maxn 25
using namespace std;
const int mod = 998244353;
int n,m,a[maxn],p[maxn][maxn],inv[maxn],ans,sum[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d",&a[i]);
	inv[0]=inv[1]=1;
	for(int i=2;i<=m;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	p[1][0]=sum[0]=1;
	for(int i=2;i<=m+1;i++)
		for(int j=i-1;j>=1;j--){
			p[i][j]=1ll*sum[j-1]*inv[i-1]%mod,sum[j]=(sum[j]+p[i][j])%mod;
			ans=(ans+1ll*p[i][j]*a[j])%mod;
		}
	printf("%d\n",ans);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值