数学专题训练3

这次是概率专练

选的题都比较基础。。因为我对概率的感觉很不到位啊啊啊啊

题目一:wikioi计算概率

地址:http://www.wikioi.com/problem/1533/

这个就是古典概型嘛。。。先枚举选的第一根。。。然后再求出第选二根后和小于等于l的方案个数。然后答案很自然就是 这些方案个数的和/n*(n-1)了。。

考虑到n稍大。。求方案数不能直接暴力。。。可以选择二分来求(要注意选的第二根不能与第一根一样。。所以二分后还要判断下这些可选的木棍是否包含了第一根,包含了的话减去)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int n,ll;
int len[100000+10];
double ans=0;
int main()
{
	scanf("%d%d",&n,&ll);
	for(int i=1;i<=n;i++)scanf("%d",&len[i]);
	sort(len+1,len+n+1);
	for(int i=1;i<=n;i++)
	{
		int l=1,r=n;
		while(l<r)
		{
			int mid=l+r+1>>1;
			if(len[mid]+len[i]>ll)r=mid-1;
			else l=mid;
		}
		if(l>=i)
		{
			l--;
		}
		if(l==1&&len[1]+len[i]>ll)l--;
		ans=ans+l*1.0/(n-1);
	}
	printf("%.2f\n",ans/n);
	return 0;
}

第二题:rqnoj矩阵粉刷

地址:http://www.rqnoj.cn/problem/717

rq的某次月赛题。。。当时我是第一次参加网赛。。结果一道都不会 T_T

其实这题的题意我觉得不是太清楚。。。说的随机选两个点。。。其实选的这两个点可以是一个。。然后对应的矩形就是一个小方格了

期望方块数自然就是 每个方块的被刷到的概率*权值1 的和。

但是要粉刷k次。。。不好直接求哪个小方格被刷的概率。。。于是可以求某次不被刷的概率 p,然后1-p^k就是被刷的概率啦

然后某次不被刷的概率又要用1-某次被刷的概率来求。。

考虑到这样一个问题:对于每个方块。。。包含了它的矩形一定有两个相对的顶点在它的坐上方和右下方。。。(包括它本身所在坐标)

然后这样的矩形数基本上就是 左上角点横坐标可选方案数*纵坐标可选方案数*右下角点横坐标可选方案数*纵坐标可选方案数。

对于不为一个或一行一列的矩形。。。有四种选点的方式会选到它,对于是一行或一列的矩形只有两种,对于只有一格的矩形(也就是正好是这一格本身)只统计了一次。。。

于是我们就枚举方块(i,j)

其一次被粉刷的方案数为 4*i*j*(w-i+1)*(h-j+1)-2*(i*(w-i+1)+j*(h-j+1))+1 (先全当成普通的矩形,然后减去为一行或一列时多计算的次数,再加上多扣了的当一个格子时的次数),概率就是  上面那个式子/w*w*h*h

这题基本上就这样了。。。

#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long k,w,h;
double ans=0;
double qpow(double d,int c)
{
	if(c==0)return 1;
	if(c==1)return d;
	double ret=qpow(d,c/2);
	ret=ret*ret;
	if(c&1)ret=ret*d;
	return ret;
}
int main()
{
	scanf("%lld%lld%lld",&k,&w,&h);
	long long s=w*w*h*h;
	for(int i=1;i<=w;i++)
	{
		for(int j=1;j<=h;j++)
		{
			double tem=i*(w-i+1)*j*(h-j+1)*4-2*(i*(w-i+1)+j*(h-j+1))+1;
			tem=tem/s;
			ans+=1-qpow(1-tem,k);
		}
	}
	printf("%.0f\n",ans);
	return 0;
}

题目三:rqnoj bomb

地址:http://www.rqnoj.cn/problem/691

我觉得稍微有点高端的题。。。纠结了很久(当然对大神们来说还是水到爆啦)

这道题是一道与期望有关的dp

状态这么设计:dp[i]表示剩i个精灵时的期望操作次数(我开始设计成减少i个的期望。。。结果纠结半天)

然后就可以得出这样一个方程:

dp[i]=(dp[i]*p[0]+dp[i-1]*p[1]+dp[i-2]*p[2]+....+dp[i-5]*p[5])+1;

直接用这个方程来递推的话。。。当然不行。。。因为左右都有dp[i];

所以移个项整理一下...变成dp[i]=p[1]/(1-p[0])*dp[i-1]+...+p[5]/(1-p[0])*dp[i-5]+1/(1-p[0]);

边界是这样的:对于i<m,dp[i]=0;

考虑到数据范围有点大。。。这个递推又是线性的。于是就可以想到用矩阵乘法来优化。。。

然后基本就没什么难点了

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m;
double p[10];
double end=0;
struct Mat
{
	int x,y;
	double num[8][8];
	Mat(){memset(num,0,sizeof(num));}
	Mat operator *(const Mat &b)
	{
		Mat c;
		c.x=x;c.y=b.y;
		for(int i=1;i<=x;i++)
		{
			for(int j=1;j<=b.y;j++)
				for(int k=1;k<=y;k++)
				{
					c.num[i][j]+=num[i][k]*b.num[k][j];
				}
		}
		return c;
	}
}start,single,ele;
void readdata()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=m;i++)scanf("%lf",&p[i]);
}
double dp[5+10];
void pre()
{
	start.x=6;start.y=1;
	dp[m]=1.0/(1-p[0]);
	for(int i=m+1;i<=4;i++)
	{
		for(int j=1;j<=5;j++)if(i-j>=0)dp[i]+=(dp[j]*p[i-j]/(1-p[0]));
		dp[i]+=1.0/(1-p[0]);
	}
	start.num[1][1]=dp[4];
	start.num[2][1]=dp[3];
	start.num[3][1]=dp[2];
	start.num[4][1]=dp[1];
	start.num[5][1]=dp[0];
	start.num[6][1]=1.0/(1-p[0]);
	single.x=single.y=6;
	for(int i=1;i<=6;i++)
	{
		single.num[i][i]=1;
	}
	for(int i=1;i<=5;i++)ele.num[1][i]=p[i]/(1-p[0]);
	ele.x=ele.y=6;
	ele.num[1][6]=1;ele.num[2][1]=ele.num[3][2]=ele.num[4][3]=ele.num[5][4]=1;
	ele.num[6][6]=1;
}
Mat qpow(const Mat &d,int c)
{
	if(c==0)return single;
	if(c==1)return d;
	Mat ret=qpow(d,c/2);
	ret=ret*ret;
	if(c&1)ret=ret*d;
	return ret;
}
int main()
{
	readdata();
	pre();//计算出dp[0]到dp[4]并存入start
	if(n<=4)printf("%.1f\n",dp[n]);
	else
	{
		Mat ans=qpow(ele,n-4);
		ans=ans*start;
		printf("%.1f\n",ans.num[1][1]);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值