一些期望题
P1297 [国家集训队]单选错位
题链
我们可以发现n是2e7 那显然是线性的
我们直接对每一位考虑
1.要是a[i]=a[i-1] 那么我们正确的概率就是1/a[i]
2.要是a[i]>a[i-1] 那么我们必须选到a[i-1]的那些个 1/a[i-1]那么我们a[i]的选择就有a[i-1]个概率是1/a[i] = 1/a[i]
3.a[i]<a[i-1]同理 我们要选到1/a[i]个 左边有a[i]个概率是1/a[i-1]
所以我们的答案就是/sigma 1/max(a[i],a[i-1])
int n,A,B,C,a[N];
void solve(){
a[n+1]=a[1];
double ans=0;
for(int i=2;i<=n+1;i++){
ans+=1.0/max(a[i],a[i-1]);
}
printf("%.3lf",ans);
}
void init(){
scanf("%d%d%d%d%d", &n, &A, &B, &C, a + 1);
for (int i = 2; i <= n; i++)
a[i] = ((long long) a[i - 1] * A + B) % 100000001;
for (int i = 1; i <= n; i++)
a[i] = a[i] % C + 1;
}
P5104 红包发红包
当然也可以是直接乱搞结论
我们肯定知道每次散出去的期望是w/2
下一次就是w/2/2
这样就搞出来了w/(2^k)
void solve(){
cin>>w>>n>>k;
cout<<w*inv(qmi(2,k,mod))%mod;
}
2020 南京 F.Fireworks
同样是乱搞
我们知道制作k次然后成功的概率是
p=成功概率
q=1-p=失败概率
1-q^k 只有全部失败才算失败 否则都算成功 这个就是成功概率
成功概率的倒数就是期望的轮数
我们要求的是期望的最小值
我们制作k个要的时间是(kn+m)
所以E=(kn+m)/(1-q^k)
暴力枚举肯定会T
我们随便猜个凹函数三分即可
//upt
我们也可以公式化 dp的来看看
显然我们这列就要搞个dp
dp[i]表示该状态下走到结束状态的期望步数
那么i就只有0/1两种状态 分别表示成功失败
这样我们dp[1]=0;
答案就是要dp[0]
dp[0]=((kn+m)[时间]+dp[1])(1-qk)[概率]+(qk)((kn+m)[时间]+dp[0])
然后移项就可以搞出
dp[0]=(k*n+m)/(1-q^k)
double f(int k){
return (k*n+m)*1.0/(1.0-pow(1-p,k));
}
void solve(){
cin>>n>>m;
cin>>p;
p/=10000;
int l=1,r=2e5;
double lans,rans;
while(l < r) {
int lmid = l + (r - l) / 3;
int rmid = r - (r - l) / 3;
lans = f(lmid),rans = f(rmid);
if(lans <= rans) r = rmid - 1;
else l = lmid + 1;
}
printf("%.8lf\n",min(lans,rans));
}
总结这样不用dp的题 基本上都是与前面做的操作无关的
然后就会有一些类似于计数的概率题出现
P6154 游走
问的是每条路径长度的期望
我们可以直接将走过的长度记录下来
再将路径数量记录下来
然后相除就是期望了
vector<int>g[N];
void dp(int u){
if(f[u])return;
b[u]=1;
for(auto v:g[u]){
dp(v);
(f[u]+=f[v]+b[v])%=mod;
(b[u]+=b[v])%=mod;
}
}
void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
g[u].push_back(v);
}
for(int i=1;i<=n;i++)if(!f[i])dp(i);
int ans=0,s=0;
for(int i=1;i<=n;i++)(ans+=f[i])%=mod;
for(int i=1;i<=n;i++)(s+=b[i])%=mod;
cout<<ans*inv(s)%mod<<endl;
}
P3802 小魔女帕琪
还有一些和组合数相关了
我们考虑在那些地方可以放置这7个魔法
然后我们让这连续的七个位置独立出来
这样就相当于上面的方案数/总和
但是这里的总和不好算出来
我们考虑直接搞出期望
我们知道有N-6个地方可以放置
而且这7个位置可以全排列就是7!
最后我们把这七个的概率搞出来
利用线性性E[CX]=C*E[x]就可以了
void solve(){
vector<int>a(7);
double s=1;
int sum=0;
for(int i=0;i<7;i++)cin>>a[i],sum+=a[i],s*=a[i]*(i+1);
for(int i=sum;i>=sum-5;i--)s/=i;
printf("%.3lf",s);
}
P1654 OSU!
这个就是春春线性性应用了
知道E[(x+1)3]-E[x3]=E[3x^2]+E[3x]+1
我们维护这样一个E[x^2]和E[x]即可
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i];
x1[i]=(x1[i-1]+1)*p[i];
x2[i]=(x2[i-1]+2*x1[i-1]+1)*p[i];
x3[i]=x3[i-1]+(3*x2[i-1]+3*x1[i-1]+1)*p[i];
}
printf("%.1lf",x3[n]);
}
UVA1639 糖果 Candy
还有一些精度优化
对于这题我们显然常规的终点要吗是一个盒子为空是终点
或者第二个盒子为空是终点
我们第一个盒子为空:
然后第二个盒子只吃了i个
相当于又n+1个间隙 放i个二 就是Cn+1 i 再乘上p^(n+1)最后发现了没有了还要再打开一次
在乘上q^i次方
但是这样Cn+i*i 显然是会爆ll的
我们db和ll没法好计算
我们就可以对这个算式整体取ln
就变成了
然后预处理阶乘的ln即可
void solve(){
for(int i=1;i<=400000;++i) {
F[i] = F[i-1] + log(i);
}
int cnt = 0;
while(scanf("%d%lf",&n,&p)!=EOF) {
double EX = 0.0;
q = log(1-p);
p = log(p);
for(int i=0;i<=n;++i) {
w_1 = F[2*n-i] - F[n] - F[n-i] + (n+1) * p + (n-i) * q;
w_2 = F[2*n-i] - F[n] - F[n-i] + (n+1) * q + (n-i) * p;
EX += i * ( exp(w_1) + exp(w_2) );
}
printf("Case %d: %.6lf\n",++cnt,EX);
}
}
C. Bubble Strike
题链
显然我们可以分类讨论
也就只有4种情况
3个会 2个会 1个会 0个会
然后分别计算出概率 我们假设会的图有x个
3个会的 肯定最终结果也是选到会的 x/n(x-1)/(n-1)(x-2)/(n-2)
2个会的 那显然我把不会的那个筛掉 那么我还是肯定会的 x/n(x-1)/(n-1)(n-x)/n-2但是这里要3是因为我们顺序可以交换
1个会的 我只能筛掉一个 还有就是对半开的概率要吗选到不会的要吗就是会的
x/n(n-x)/(n-1)(n-x-1)/(n-2)这里也可以交换顺序所以3
没有会的不管怎么筛都是不会的 所以我们就不计算概率进去了
然后会发现这是个三次函数我们求0点 发现n只有1e3 直接暴力枚举即可
int f(int x) {
db res=0;
if(x>=2)res+=x*(x-1)*(x-2);
if(x>=1)res+=3*x*(x-1)*(n-x);
if(n>=x+1)res+=(3*x*(n-x)*(n-x-1))*1.0/2;
return res>=m*n*(n-1)*(n-2);
}
void solve(){
cin>>n>>m;
for(int i=0;i<=n;i++){
if(f(i)){
cout<<i<<endl;
return;
}
}
}
D. Ilya and Escalator
题链
显然范围2000
我们考虑dp
dp[i][j]表示第i秒有j人在里面的概率 然后最后我们t秒直接用概率人数就是期望了
初始化 f[0][0]=1
考虑转移
显然我们
dp[i][j]=dp[i-1][j-1]p+dp[i-1][j]*(1-p)
但是这个转移要是j0时 我们不能进行前面一个转移
要是jn时 我们后面的那个转移就是百分之百的
db dp[2010][2010];
int n,t;db p;
void solve(){
dp[0][0]=1;
cin>>n>>p>>t;
for(int i=1;i<=t;i++){
for(int j=0;j<=max(i,n);j++){
if(j==0)dp[i][j]=dp[i-1][j]*(1.0-p);
else if(j==n)dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*p;
else dp[i][j]=dp[i-1][j-1]*p+dp[i-1][j]*(1.0-p);
}
}
db ans=0;
for(int j=0;j<=n;j++)ans+=dp[t][j]*j;
printf("%.8lf",ans);
}