概率与期望专项

大概是概率期望综合题解,注意在做题的时候积累套路,别再看见概率期望题一点思路没有了!!

[ABC300E] Dice Product 3

考虑到最终所得到的整数是由骰子上的数相乘所得,可以考虑分解因数。

1,2,3,4,5,6 中质因子为 2,3,5 ,所以最终的答案也一定是由 2,3,5 相乘得来。

由此可以判断无解情况,即分解出的因数中含除 2,3,5 以外的其他因子。

剩余情况考虑dp。

\(dp[i][j][k]\) 表示使用 \(i\)\(2\)\(j\)\(3\)\(k\)\(5\) 相乘得到的答案。

分别对摇出2,3,4,5,6,的不同情况进行转移即可。(如以下是2的转移)

\[dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k]*inv[5]) \]

代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a,b,c;
const int mod=998244353;
int dp[105][105][105];
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1){
			ans=ans*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	while(n%2==0)
		n/=2,a++;
	while(n%3==0)
		n/=3,b++;
	while(n%5==0)
		n/=5,c++;
	if(n!=1){
		cout<<0<<endl;
		return 0;
	}
		
	int g=qpow(5,mod-2);//预处理5的逆元 
	dp[0][0][0]=1;
	for(int i=0;i<=a;i++){
		for(int j=0;j<=b;j++){
			for(int k=0;k<=c;k++){
				dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k]*g)%mod;
				dp[i][j+1][k]=(dp[i][j+1][k]+dp[i][j][k]*g)%mod;
				dp[i+2][j][k]=(dp[i+2][j][k]+dp[i][j][k]*g)%mod;
				dp[i][j][k+1]=(dp[i][j][k+1]+dp[i][j][k]*g)%mod;
				dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*g)%mod;
			}
		}
	}
	cout<<dp[a][b][c]<<endl;
	return 0;
} 

[ABC263E] Sugoroku 3

和上题相似,但数字的得到方式变成加法。

由于所求为期望,我们考虑逆推。

\(dp[i]\) 表示由当前 \(i\) 走到 \(n\) 的期望步数。

\(i\) 个格子到第 \(n\) 个格子的期望步数等于它能直接到达的格子的期望步数+1再除以 \(i\) 能到达的格子数量(即转移概率)。

即:

\[dp_i=\sum_{j=0}^{a_i} (dp_{i+j}+1) \div(a_i+1) \]

化简得:

\[dp_i\cdot (a_i+1) = a_i+1+\sum_{j=1}^{a_i} dp_{i+j} \]

最后可以得到:

\[dp_i=(a_i+1+\sum_{j=1}^{a_i} dp_{i+j}) \div a_i \]

考虑使用后缀和维护 \(\sum_{j=1}^{a_i} dp_{i+j}\) 的值。

代码
//逆推,感觉不像上道题那样可以分解因子啊
/*从n-1倒着往前推(因为每个格子只能向右跳)
dp[i]=它能直接到达的格子的期望步数+1,再除以i能到达的各自的数量*/ 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
const int mod=998244353;
int n,inv[maxn],dp[maxn],a[maxn],sum[maxn];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>a[i];
	}
	inv[1]=1;
	for(int i=2;i<maxn;i++){
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=n-1;i>=1;i--){
		int t=(sum[i+1]-sum[i+a[i]+1]+mod)%mod;
		dp[i]=(a[i]+1+t)%mod*inv[a[i]]%mod;
		sum[i]=(sum[i+1]+dp[i])%mod;
	}
	cout<<dp[1]<<endl;
} 

[ABC276F] Double Chance

数据结构优化题。

首先从题目中的 \(\max(a_i,a_j)\) 中入手,考虑到第 \(x\) 次询问,答案是 $ \dfrac{\sum_{i=1}^x \sum_{j=1}^x \max(a_i,a_j)}{x^2}$ 。然后分类,把 \(1\)\(i-1\) 的球分为两类,一类权值小于 \(val_i\) ,个数为 \(cnt\) 个;一类权值大于 \(val_i\) ,所有数的和为 \(sum\) 。设分子为 \(c_x\) ,那么只需要计算新加入的数 \(a_x\) 对其的影响。对于第一类,\(\max(a_i,a_x)=a_x\) ,否则\(\max(a_i,a_x)=a_i\)。注意选到相同的两个数调换顺序为两种情况。

转移方程:$$c_x=c_{x-1}+cnt\times a_x\times2+sum\times2+a_x$$

代码
//分类讨论? 
/*考虑到总共的情况总是n^2的,每次只需要考虑当前取出来的两个对答案有没有影响*/
//数据结构优化 
/*设b[x]表示第x操作的期望权值
b[x]=x^-2*cinma max(a[i],a[j])
递推计算后半部分的值
开两个树状数组分别维护a[1]--a[x-1]中<=a[x]的个数cnt
以及 a[1]--a[x-1]中>a[x]的数的和sum
c[x]=c[x-1]+cnt*a[x]*2+sum*2+a[x]*/ 
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define int long long
using namespace std;
const int maxn=2e5+10;
const int mod=998244353;
int n,a[maxn];
int cnt[maxn],sum[maxn],inv[maxn];
void add(int x,int val){
	for(int i=x;i<=maxn;i+=lowbit(i))
		cnt[i]+=val;
}
void addsum(int x,int val){
	for(int i=x;i<=maxn;i+=lowbit(i)){
		sum[i]+=val;
		sum[i]%=mod;
	}
}
int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=cnt[i];
	}
	return ans;
}
int querysum(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=sum[i];
		ans%=mod;
	}
	return ans%mod;
}
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1){
			ans=ans*a%mod;			
		}
		a=a*a%mod;
		b>>=1;
	} 
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int c=0;
	int t=0;
	for(int i=1;i<=n;i++){
		c=(c+query(a[i])*a[i]%mod*2%mod)%mod;
		int qwq=((t-querysum(a[i]))%mod+mod)%mod;
		//我能把前缀和当单个查询来写。。。 
		c=(c+qwq*2%mod+mod)%mod;
		c=(c+a[i])%mod;
		cout<<c*qpow(i*i%mod,mod-2)%mod<<endl;
		add(a[i],1);
		addsum(a[i],a[i]);
		t=(t+a[i])%mod;
	}
}

[cf869C] The Intriguing Obsession

入手点全在题面里。(收获是遇见不会做的题,再读一遍题。

首先,由“任意两个不同的、颜色相同的小岛的最短距离要大于等于三”,可以得出“同色岛不能相连,一个岛屿不能和另外两个同色岛同时连”。

此时情况只有三种,\(ab\) 相连, \(bc\) 相连,\(ca\) 相连。

然后考虑计算答案,从 \(a\)\(b\) 中各挑 \(i\) 个的方案数=从 \(a\) 中挑 \(i\) 个的方案数 \(*\)\(b\) 中挑 \(i\) 个的方案数 \(*\) \(i\) 个数的排列情况总数,分别计算,最终相乘即可。

代码大概是思路的形成和实现的总和。

代码
//两个颜色相同的小岛最短距离>=3 
/*得出结论,同色岛不能相连,一个岛屿不能和另外两个同色岛同时连
此时情况只有三种,a-b,b-c,a-c*/ 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=5005;
int a,b,c;
int f[maxn],g[maxn],C[maxn][maxn];
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1){
			ans=ans*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int qC(int n,int m){
	if(m>n)
		return 0;
	return f[n]*g[m]%mod*g[n-m]%mod; 
}
//从a与b中各挑i个方案数 =从a中挑i个数的方案数*从b中挑i个数的方案数 *i个数的排列情况总数。
int solve(int a,int b){
	int x=min(a,b),ans=1;
	for(int i=1;i<=x;i++){
		ans=(ans+C[a][i]*C[b][i]%mod*f[i]%mod)%mod;
	}
	return ans;
}
void init(){
	f[0]=g[0]=1;
	for(int i=1;i<maxn;i++){
		f[i]=f[i-1]*i%mod;//阶乘
		g[i]=g[i-1]*qpow(i,mod-2)%mod;//阶乘逆元 
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>a>>b>>c;
	init();
	for(int i=1;i<maxn;i++){
		for(int j=0;j<=i;j++){
			C[i][j]=qC(i,j);
		}
	}
	cout<<(solve(a,b)*solve(a,c)%mod*solve(b,c))%mod<<endl;
	
}

Assimilation IV

一点思路没有的神仙题。

首先点亮城市的顺序随机,而要计算第 \(n\) 秒的瞬间被照亮的点数的期望值,所以考虑将贡献拆开,对每个点单独计算它被选中的概率再求和即为答案。(类比线性期望可加!积累这个套路!)

其次正难则反,对于每一个点来说在第 \(n\) 秒被点亮的情况有很多,较难计算,可以考虑求在第 \(n\) 秒不会被点亮的方案数。

对于一个点是否被点亮,可以通过点亮城市的顺序确定。

\(dis_{i,j}\) 表示距离第 \(i\) 个点 \(j\) 个距离单位的点的个数。

第一个被点亮的城市,从这里出发的光束会走 \(n\) 个距离单位,如果不想被点亮,第一个位置只能放距离当前枚举的点长度为 \(n+1\) 的城市,同理,第二个位置可以放距离这个点单位为 \(n\) 的城市意外,还可以放第一次没有选的 \(dis_{i,n+1}-1\) 个城市。

对于每一个点,求出不能到达的方案数 \(cnt\) ,对答案的贡献即为:$$1- \dfrac {1-cnt} {n!}$$

点击查看代码
//首先进行拆点计算 ,每个点被选中的概率求和 
//注意不是城市 
//正难则反 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=5e4+10;
int n,m,d[30][maxn];
int dis[maxn][30],ans;//表示距离第i个点j个单位的点的个数 
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1){
			ans=ans*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>d[i][j];
			dis[j][d[i][j]]++; 
		} 
	}
	int fac=1,sum=0,nott=1;
	//nott表示对于每一个点不能到达的方案
	for(int i=1;i<=n;i++){
		fac=fac*i%mod;
	} 
	fac=qpow(fac,mod-2);
	for(int i=1;i<=m;i++){
		sum=0,nott=1;
		for(int j=1;j<=n;j++){
			sum+=dis[i][n+2-j];
			nott=nott*sum%mod;
			sum--;//减去使用过的那个 
		}
		ans=((ans+1ll-fac%mod*nott)%mod+mod)%mod; 
	} 
	cout<<ans<<endl;
}

Lengthening Sticks

看起来是简单题实则很不好想。

暴力枚举显然是不现实的,正难则反,我们考虑容斥原理,使用所有组合的方案数减去不合法的方案数。

对于不合法的方案数一定满足 \(a\) , \(b\) , \(c\) 中有一个数大于另外两个数的和。分别考虑 \(a\) , \(b\) , \(c\) 做三角形最长边的情况,比如取 \(c\) 做最长边,那么枚举加的长度 \(i\) ,最多还有 $\min(c+i-a-b,l-i) $ 分配给 \(a\)\(b\),乘法原理计算即可。

考虑总攻的方案数,也就是把最多 \(l\) 个小球放在 \(3\) 个有标记的盒子,允许不放,考虑插板法,枚举 \(l\) ,答案也就是 \(C(l+2,2)\)

点击查看代码
//想到了怎么判断方案不合法,但是没想到容斥原理 
//对于不合法的方案分别考虑abc做三角形最长边的情况
//比如取c做最长边,枚举加的长度,那么还有最多min(c+i-a-b,l-i)的部分可以分配给ab 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=3e5+10;
const int mod=998244353;
int a,b,c,l,ans;
int solve(int x,int y,int z){
	int tot=0;
	for(int i=0;i<=l;i++){
		int tt=min(z+i-x-y,l-i);
		if(tt>=0){
			tot+=(tt+2)*(tt+1)/2;
		}
	}
	return tot;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>a>>b>>c>>l;
	for(int i=0;i<=l;i++){
		ans+=(i+2)*(i+1)/2;
	}
	ans-=solve(a,b,c)+solve(b,c,a)+solve(a,c,b);
	cout<<ans<<endl;
}

ABC323E Playlist

dp。所求概率,考虑顺推。

\(dp_i\) 表示第 \(i\) 秒歌曲刚好播完,即开启下一首歌的概率。

因为随机放下一首歌的概率是相等的,因此有:$$dp_{i+t_i}=dp_{i+t_j}+\frac{1}{n}dp_i$$。

在统计答案时,将 \(dp_{X-t_1+1}\)\(dp_{X}\) 的值加起来,乘上 \(\frac{1}{n}\)即可。

其他见代码。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=1e4+10;
int n,x,a[maxn],dp[maxn];
//设dp[i]表示i时刻正好结束播出某首歌的总概率
 
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1){
			ans=ans*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>x;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	} 
	dp[0]=1;
	int t=qpow(n,mod-2);
	for(int i=0;i<=x;i++){
		for(int j=1;j<=n;j++){
			if(i>=a[j]){
				dp[i]=(dp[i]+dp[i-a[j]]*t)%mod;
			}
		}
	}
	int qwq=0;
	for(int i=max(0ll,x-a[1]+1);i<=x;i++){
		qwq+=dp[i];
		qwq%=mod;
	}
	cout<<qwq*t%mod<<endl;
} 

Maxim and Restaurant

posted @ 2024-05-07 21:29  Aapwp  阅读(5)  评论(0编辑  收藏  举报