「some」概率和期望习题总结

Problem 1

绿豆蛙的归宿

\(dp[u]\)表示从\(u\)\(n\)所经过的路径总长度的期望,从\(u\)出发有\(k\)条边,指向的结点为\(v_i\),边长分别为\(w_i\),则有

\[dp[u]=\frac{1}{k}\;\sum_{i=1}^{k}\;(dp[v_i]+w_i) \]

显然\(dp[n]=0\)

建反图,跑\(topo\)就可以了

Code:

#include<cstdio>
#include<queue>
#define MAX 100001
#define re register
namespace OMA
{
   int n,m;
   double dp[MAX];
   double du[MAX],out[MAX];
   int cnt=1,head[MAX];
   struct node
   {
     int next;
     int to;
     int w;
   }edge[MAX<<1];
   std::queue<int>q;
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   inline void add(int u,int v,int w)
   {
     edge[++cnt].next = head[u];
     edge[cnt].to = v;
     edge[cnt].w = w;
     head[u] = cnt;
   }
   void in()
   {
     dp[n = read()] = 0,m = read();
     for(re int i=1; i<=m; i++)
     {
       int u=read(),v=read(),w=read();
       add(v,u,w);
       du[u]++,out[u]++;
     }
   }
   void ans()
   {
     q.push(n);
     while(!q.empty())
     {
       int k = q.front();
       q.pop();
       for(re int i=head[k]; i; i=edge[i].next)
       {
         int w = edge[i].w;
         int to = edge[i].to;
         dp[to] += (dp[k]+w)/out[to];
         if(!--du[to])
         { q.push(to); }
       }
     }
     printf("%.2lf\n",dp[1]);
   }
   signed main()
   {
     in();
     ans();
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 2

聪聪和可可

题解

Problem 3

OSU!

3次的不好想,则从1次入手,设\(a[i]\)表示前i位中第\(i\)位为1时的期望分数,则

\(a[i]=(a[i-1]+1)\times p\),

\((a+1)^2=a^2+2\times a+1\)可递推到2次,

\(b[i]=(b[i-1]+2\times a[i-1]+1)\times p\),

同理,则3

\(dp[i]=(dp[i-1]+3\times a[i-1]+3\times b[i-1]+1)\times p+dp[i-1]\times (1-p)\)
化简后则有

\(dp[i] = dp[i-1]+(3\times a[i-1]+3\times b[i-1]+1)\times p\)

如果还是不理解推导过程的话,可以去看这篇blog

注意他3次的式子的左括号的位置是错的,重点看推导过程就好

Code:

#include<cstdio>
#include<cmath>
#define MAX 100001
#define re register
namespace OMA
{
   int n;
   //double a,b,ans;
   double dp[MAX];
   double a[MAX],b[MAX];
   inline double dread()
   {
     int s=0,w=1,k=0,a=0,b=0;
     char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while((ch>='0'&ch<='9')||ch=='.')
     {
       if(ch=='.'){ a=1; }
       else if(!a)
       { s=s*10+ch-'0'; }
       else
       { k=k*10+ch-'0'; b++; }
       ch = getchar();
     }
     return (pow(0.1,b)*k+s)*w;
   }
   signed main()
   {
     scanf("%d",&n);
     for(re int i=1; i<=n; i++)
     {
       double p=dread();
       a[i] = (a[i-1]+1)*p;
       b[i] = (b[i-1]+2*a[i-1]+1)*p;
       dp[i] = dp[i-1]+(3*a[i-1]+3*b[i-1]+1)*p;
       /*ans = ans+(3*a+3*b+1)*p;
       b = (b+a*2+1)*p;
       a = (a+1)*p;*/
       // 两种打法都可以,第二种空间小,但时间都是差不多的
     }
     printf("%.1lf",dp[n]);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 4

Red is good

状态转移方程还是很好推的

\(dp[i][j]\)表示选了\(i\)张红牌,\(j\)张黑牌时的最优策略,
则有

\(dp[i][j]=\max(\frac{i}{i+j}\times(dp[i-1][j]+1)+\frac{j}{i+j}\times(dp[i][j-1]-1),0)\),其中\(dp[i][0]=i\)

至于四舍五入,输出时处理一下进位就好

同时注意,强制转换类型时一定要加括号!!!
因为这个调了一个半小时QAQ

某oj上卡空间,请使用滚动数组,但我不会QAQ
Code:

#include<cstdio>
#define re register
#define MAX 5001
namespace OMA
{
   int r,b;
   double ans,dp[MAX][MAX];
   inline double max(double a,double b)
   { return a>b?a:b; }
   signed main()
   {
     scanf("%d%d",&r,&b);
     for(re int i=1; i<=r; i++)
     {
       dp[i][0] = i;
       for(re int j=1; j<=b; j++)
       {
         ans=0;
         ans += (dp[i-1][j]+1)*((double)i/(i+j));
         ans += (dp[i][j-1]-1)*((double)j/(i+j));
         dp[i][j] = max(ans,0);
       }
     }
     ans = dp[r][b]-0.0000005;
     printf("%.6lf",ans);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 5

守卫者的挑战链接可能会挂
题解

Problem 6

Easy

有点类似第三题

\(l[i]\)表示以\(i\)结尾时的长度期望,\(dp[i]\)表示第\(i\)位的分数期望,
则会有以下三种情况:

\(ch[i]=o\) 时, \(l[i]=l[i-1]+1\) , 同第三题,通过\(\left(l+1\right)^2=l^2+2 \times l+1\)推出 \(dp[i]=dp[i-1]+2\times l[i-1]+1\)

\(ch[i]=x\) 时, \(l[i]=0\) , \(dp[i]=dp[i-1]\)

\(ch[i]=?\) 时, \(l[i]=\left(l[i-1]+1\right)/2\) , \(dp[i]=\left(2\times dp[i-1]+2\times l[i-1]+1\right)/2\)

Code:

#include<cstdio>
#define re register
#define MAX 300001
namespace OMA
{
   int n;
   char ch[MAX];
   double l[MAX],dp[MAX];
   signed main()
   {
     scanf("%d",&n),scanf("%s",ch+1);
     for(re int i=1; i<=n; i++)
     {
       if(ch[i]=='o')
       { 
         l[i] = l[i-1]+1;
         dp[i] = dp[i-1]+2*l[i-1]+1;
       }
       else if(ch[i]=='x')
       { dp[i] = dp[i-1]; }
       else if(ch[i]=='?')
       {
         l[i] = (l[i-1]+1)/2;
         dp[i] = dp[i-1]+(2*l[i-1]+1)/2;
       }
     }
     printf("%.4lf",dp[n]);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 7

单选错位

我tm就是狗屎
好吧,我还是太菜了

对答案的情况分类讨论,则有以下三种:

\(a_{i-1}=a_i\),则做对的期望为\(\frac{1}{a_i}=\frac{1}{a_{i-1}}\)

\(a_{i-1}>a_i\),则答案在\(1\;~\;a_i\)中的概率为\(\frac{a_i}{a_{i-1}}\),则做对的期望为\(\frac{a_i}{a_{i-1}}\times \frac{1}{a_i}=\frac{1}{a_{i-1}}\)

\(a_{i-1}<a_i\),则答案在\(1\;~\;a_{i-1}\)中的概率为\(\frac{a_{i-1}}{a_i}\),则做对的期望为\(\frac{a_{i-1}}{a_i}\times \frac{1}{a_{i-1}}=\frac{1}{a_i}\)

综上

\[ans=\sum_{i=1}^{n}\;\frac{1}{\max(a_i,a_{i-1})} \]

Code:

#include<cstdio>
#define re register
#define MAX 10000001
namespace OMA
{
   int n,A,B,C;
   int a[MAX];
   double ans;
   const int p=100000001;
   inline int max(int a,int b)
   { return a>b?a:b; }
   signed main()
   {
     scanf("%d%d%d%d%d",&n,&A,&B,&C,a+1);
     for(re int i=2; i<=n; i++)
     { a[i] = ((long long)a[i-1]*A+B)%p; }
     for(re int i=1; i<=n; i++)
     { a[i] = a[i]%C+1; }
     //for(re int i=1; i<=n; i++)
     //{ printf("%d ",a[i]); }
     a[0] = a[n];
     for(re int i=1; i<=n; i++)
     { ans += (double)1/max(a[i],a[i-1]); }
     printf("%.3lf",ans);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 8

列队春游链接可能会挂

zjx写了一篇,所以不打算写了,其实是太懒了

所以贴个式子和代码吧

\[ans=\sum_{i=1}^{top}\;\frac{cnt[i]\times \left(n+1\right)}{n-sum+1},\ \ sum\;+=cnt[i] \]

其中\(top\)为预处理出来的最高的高度,\(cnt[i]\)表示当前高度的有多少人,\(sum\)为比当前这个高度要矮的总人数

其实可以不用预处理,范围小,倒是无所谓

Code:

#include<cstdio>
#define MAX 1001
#define re register
namespace OMA
{
   int n;
   int top,sum;
   double ans;
   int high,cnt[MAX];
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   inline int max(int a,int b)
   { return a>b?a:b ;}
   signed main()
   {
     n=read();
     for(re int i=1; i<=n; i++)
     { top = max(top,high = read()); cnt[high]++; }
     for(re int i=1; i<=top; i++)
     {
       if(cnt[i])
       {
         ans += (double)cnt[i]*(n+1)/(n-sum+1);
         sum += cnt[i];
       }
     }
     printf("%.2lf\n",ans);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 9

矩形粉刷链接同样可能会炸

hjz写了一篇,所以,没错,我又鸽了

所以化简搞麻烦了一下式子

\(a=h^2\times \left[{(w-i)^2+(i-1)^2}\right]+w^2\times \left[(h-j)^2+(j-1)^2\right]\)

\(b=\left[(h-j)^2+(j-1)^2\right]\times \left[(i-1)^2+(w-i)^2\right]\)

\[ans=\sum_{i=1}^{w}\sum_{j=1}^{h}{a}-{b} \]

记得容斥

Code:

#include<cstdio>
#define re register
namespace OMA
{
   int k,w,h;
   double ans,all;
   inline double quickpow(double a,int b)
   {
     double ans=1;
     while(b)
     {
       if(b&1)
       { ans = ans*a; }
       a = a*a;
       b >>= 1;
     }
     return ans;
   }
   inline double mul(double a)
   { return a*a; }
   inline double solve(int i,int j)
   {
     double now;
     double a,b/*,c,d*/;
     /*a = (double)(mul((w-i)*h)+mul((h-j)*w));
     b = (double)(mul((i-1)*h)+mul((j-1)*w));
     c = (double)(mul((h-j)*(i-1))+mul((w-i)*(j-1)));
     d = (double)(mul((i-1)*(j-1))+mul((w-i)*(h-j)));*/
     a = (double)(mul(h)*(mul(w-i)+mul(i-1))+mul(w)*(mul(h-j)+mul(j-1)));
     b = (double)(((mul(h-j)+mul(j-1)))*(mul(i-1)+mul(w-i)));
     now = 1-(double)(quickpow((a-b)/all,k));
     return now;
   }
   signed main()
   {
     scanf("%d%d%d",&k,&w,&h);
     all = mul(w*h);
     for(re int i=1; i<=w; i++)
     {
       for(re int j=1; j<=h; j++)
       { ans += solve(i,j); }
     }
     printf("%.0lf\n",ans);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 10

卡牌游戏

直接求\(n\)人中每个人的获胜概率不好求,所以从前往后推

\(dp[i][j]\)表示还剩下\(i\)个人时,第\(j\)个人获胜的概率
,现在选择的牌使第\(p\)个人淘汰,所以

如果\(p>j\),则在新的环中,第\(j\)个人的位置为\(i-p+j\)

如果\(p<j\),则在新的环中,第\(j\)个人的位置为\(j-p\)

则有

\[dp[i][j]\;=\sum_{k=1}^{m} \begin{cases} dp[i-1][i-p+j]/m\;,&\text{if p>j}\\ 0 \;,&\text{if p=j} \\ dp[i-1][j-p]/m\;, &\text{if p<j} \end{cases} \]

最后记得输出 \(\color{Red}{!\;!\;!}\)

Code:

#include<cstdio>
#define MAX 51
#define re register
namespace OMA
{
   int n,m;
   int num[MAX];
   double dp[MAX][MAX];
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   signed main()
   {
     n=read(),m=read();
     for(re int i=1; i<=m; i++)
     { num[i] = read(); }
     dp[1][1] = 1;
     for(re int i=2; i<=n; i++)
     {
       for(re int j=1; j<=i; j++)
       {
         for(re int k=1; k<=m; k++)
         {
           int pos = num[k]%i;
           if(!pos)
           { pos = i; }
           if(pos>j)
           { dp[i][j] += (double)dp[i-1][i-pos+j]/m; }
           else
           { dp[i][j] += (double)dp[i-1][j-pos]/m; }
         }
       }
     }
     for(re int i=1; i<=n; i++)
     { printf("%.2lf%% ",dp[n][i]*100); }
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 11

换教室

\(dp[i][j][0]\)表示到第\(i\)节课,一共换了\(j\)次,第\(i\)节课没换,所耗费的体力值的最小期望值

\(dp[i][j][1]\)表示到\(i\)节课,一共换了\(j\)次,第\(i\)节课换了,所耗费的体力值的最小期望值

状态转移方程太长了,懒得打了,直接去看代码里的吧,还是挺好理解的

最后的答案

\[ans=\min\left(ans,\min\left(dp[n][i][0],dp[n][i][1]\right)\right), \left(i\in[0,m]\right) \]

预处理两点间最短路径时,因为点数很少,所以直接\(floyd\)就可以了

Code:

#include<cstdio>
#define MAX 2001
#define re register
#define top 11451419
namespace OMA
{
   int n,m,v,e;
   double way[MAX][MAX];
   int c[MAX],d[MAX];
   double ans=top,k[MAX];
   double dp[MAX][MAX][2];
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   inline double min(double a,double b)
   { return a<b?a:b; }
   void begin()
   {
     for(re int i=0; i<=n; i++)
     {
       for(re int j=0; j<=m; j++)
       { dp[i][j][0] = dp[i][j][1] = top; }
     }
     for(re int i=1; i<=v; i++)
     { 
       for(re int j=1; j<=v; j++)
       { way[i][j] = top; }
     }
     for(re int i=1; i<=v; i++)
     { way[i][i] = 0; }
   }
   void in()
   {
     n=read(),m=read(),v=read(),e=read();
     begin();
     for(re int i=1; i<=n; i++)
     { c[i] = read(); }
     for(re int i=1; i<=n; i++)
     { d[i] = read(); }
     for(re int i=1; i<=n; i++)
     { scanf("%lf",&k[i]); }
     for(re int i=1; i<=e; i++)
     {
       int u=read(),v=read();
       way[u][v] = way[v][u] = min(way[u][v],read());
       //printf("%lf\n",way[v][u]);
     }
   }
   void floyd()
   {
     for(re int k=1; k<=v; k++)
     {
       for(re int i=1; i<=v; i++)
       {
         for(re int j=1; j<=v; j++)
         { way[i][j] = min(way[i][j],way[i][k]+way[k][j]); }
       }
     }
   }
   signed main()
   {
     in();
     floyd();
     dp[1][0][0] = dp[1][1][1] = 0;
     for(re int i=2; i<=n; i++)
     {
       for(re int j=0; j<=min(i,m); j++)
       {
         double a,b;
         if(j)
         {
           a = dp[i-1][j-1][0]+k[i]*way[c[i-1]][d[i]]+(1-k[i])*way[c[i-1]][c[i]];
           b = dp[i-1][j-1][1]+k[i-1]*k[i]*way[d[i-1]][d[i]]+(1-k[i-1])*(1-k[i])*way[c[i-1]][c[i]]+k[i-1]*(1-k[i])*way[d[i-1]][c[i]]+k[i]*(1-k[i-1])*way[d[i]][c[i-1]];
           dp[i][j][1] = min(dp[i][j][1],min(a,b)); 
         }
         a = dp[i-1][j][0]+way[c[i]][c[i-1]];
         b = dp[i-1][j][1]+k[i-1]*way[d[i-1]][c[i]]+(1-k[i-1])*way[c[i]][c[i-1]];
         dp[i][j][0] = min(dp[i][j][0],min(a,b));
       }
     }
     for(re int i=0; i<=m; i++)
     { ans = min(ans,min(dp[n][i][0],dp[n][i][1])); }
     printf("%.2lf",ans);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

Problem 12

奖励关
题解
剩下的还没写QAQ

Problem 13

概率充电器

树形dp

Code

Problem 14

游走

Problem 15

XOR和路径

Problem 16

分手是祝愿

Problem 17

硬币游戏

posted @ 2021-05-26 11:17  -OMA-  阅读(144)  评论(1编辑  收藏  举报
浏览器标题切换
浏览器标题切换end