ybtoj Au 期望概率 DP

前言中的前言

  1. 由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻

  2. 由于本人过菜,不知道什么时候就 AFO 了,想给这个机房留下点什么……

  3. 如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人

  4. 后记会有一些逻辑不通,无病呻吟,像幼儿园写的话的句子,请见谅(建议跳过)

正片开始!

题面

题目传送门

前言

期望 T1,水题

正文

设长度为 i 的序列的分数为 fi,当前连续的 1 的长度为 xi

注意上面两个都不是期望

若第 i 位成功,fi=fi1+(xi1+1)3(xi1)3

不成功,fi=fi1

根据全期望公式(对于全期望公式,我的理解是分类讨论)

E(fi)=pi×E(fi1+(xi1+1)3(xi1)3)+(1pi)×E(fi1)=E(fi1)+3piE(xi12)+3piE(xi1)+pi

显然 E(xi)=piE(xi1+1)

E(xi2)=piE((xi1+1)2)+(1pi)E(0)=piE(xi12)+2piE(xi1)+pi

这里也是全期望公式

至此,所有变量都可求

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
double p[N],a[N],b[N],f[N];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p[i];
        a[i]=(a[i-1]+1)*p[i];
        b[i]=(b[i-1]+2*a[i-1]+1)*p[i];
        f[i]=f[i-1]+(3*b[i-1]+3*a[i-1]+1)*p[i];
    }
    printf("%.1lf",f[n]);
    return 0;
}

后记

心空,心虚

题面

题目传送门

前言

期望 T2,想咕题

但我的良知告诉我不能咕

好吧,其实也没那么想咕

这题当时看着挺难的还问了才队……

正文

其实就是维护一个期望逆序对数

考虑一个人,他可以选 k 本书

第一本书,第一轮被选的概率为 p

第二轮为 p×(1p)k 因为前 k 个他都没选

第三轮为 p×(1p)2k

……

所以第一本书被选的概率为 p+p×(1p)k+p×(1p)2k+

发现这玩意是个等比数列

求和公式:Sn=a1qn1q

显然此时公比<0

S=limnSn=limna1qn1q=a1q

好的,那么第一本书被选的概率就是 p1(1p)k

可以发现第二本书就是在这基础上×(1p),以此类推

最后用树状树组维护一下逆序对即可

代码

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,m;
struct node{int a,b;}d[N];
long double p,ans,e[N],cnt[N];
bool cmp(node x,node y){
    if(x.a==y.a)return x.b<y.b;
    return x.a<y.a;
}
int lowbit(int x){return x&-x;}
void add(int x,long double dd){
    for(;x<=n;x+=lowbit(x))e[x]+=dd;
}
long double query(int x){
    long double res=0;
    for(;x;x-=lowbit(x))res+=e[x];
    return res;
}
int main(){
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)cnt[i]=1;
    for(int i=1;i<=m;i++){cin>>d[i].a>>d[i].b;cnt[d[i].a]*=(1-p);}
    d[0].a=0,d[0].b=0;
    sort(d+1,d+1+m,cmp);
    long double S=1;
    for(int i=1;i<=m;i++){
        if(d[i].a!=d[i-1].a)S=1;
        long double w=S*p/(1-cnt[d[i].a]);
        ans+=(query(n)-query(d[i].b))*w;
        add(d[i].b,w);
        S*=1-p;
    }
    printf("%.2llf",ans);
    return 0;
}

后记

不敢回忆

题面

题目传送门

前言

期望 T3,水题

正文

dpi,j 表示第 i 秒,电梯上有 j 个人的期望

dpi,j=dpi1,j1×pi+dpi1,j×(1pi)

就完了

写代码时注意一下边界即可

代码

#include <bits/stdc++.h>
using namespace std;
int n,t;
long double p;
long double f[2005][2005];
int main(){
    cin>>n>>p>>t;
    f[0][0]=1;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=n;j++){
            if(j==n)f[i][j]=f[i-1][j-1]*p+f[i-1][j];
            else if(j==0)f[i][j]=f[i-1][j]*(1-p);
           else{ f[i][j]+=f[i-1][j-1]*p;
            f[i][j]+=f[i-1][j]*(1-p);}
        }
    }
    long double ans=0;
    for(int i=1;i<=n;i++){
        ans+=f[t][i]*i;
    }
    cout<<ans;
    return 0;
}

后记

好不容易自己做出来一道,你告诉我这是黄题???

题面

题目传送门

前言

期望 T4,综合题

会小讲一下高斯消元

正文

看起来是道构造题,但很容易想到让他经过的期望次数多的边编号尽量小

现在就是要求每条边经过的期望次数

gi 表示经过第 i 条边的期望次数,fi 表示经过第 i 个点的期望次数,ini 表示点 i 的出度

容易推出

gi=fuinu+fvinv i 是连接点 u,v 的边

所以现在要求每个点经过的期望次数

容易推出

fu=fvinv

特殊的当 u=1 时,需要+1,因为一开始肯定走

u=n 时,因为走到 n 就停了,所以其他点的期望不能由 n 转移来

所以让 fn=0

发现这个式子用高斯消元能做

高斯消元

代码

#include <bits/stdc++.h>
using namespace std;
const int N=125005;
int n,m,cnt,head[N],in[N];
long double a[505][505],kazuha,g[N];
struct node{int to,nxt;}e[N];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int uu[N],vv[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>uu[i]>>vv[i];
		add(uu[i],vv[i]),add(vv[i],uu[i]);
		in[uu[i]]++,in[vv[i]]++;
	}
	n--;
	for(int i=1;i<=n;i++){
		if(i==1)a[i][n+1]=-1;
		a[i][i]=-1;
		for(int j=head[i];j;j=e[j].nxt){
			int v=e[j].to;
			if(v!=n+1)a[i][v]=1.0/in[v];
		}
	}
	for(int i=1;i<=n;i++){
		int maxn=i;
		for(int j=i+1;j<=n;j++){
			if(fabs(a[j][i])>fabs(a[maxn][i]))maxn=j;
		}
		for(int j=n+1;j>=1;j--){
			swap(a[maxn][j],a[i][j]);
		}
		for(int j=n+1;j>=1;j--){
			a[i][j]/=a[i][i];
		}
		for(int j=1;j<=n;j++){
			if(j!=i){
				double temp=a[j][i];
				for(int k=1;k<=n+1;k++){
					a[j][k]-=temp*a[i][k];
				}
			}
		}
	}
	for(int i=1;i<=m;i++){
		g[i]=a[uu[i]][n+1]/in[uu[i]]+a[vv[i]][n+1]/in[vv[i]];
	}
	sort(g+1,g+m+1);
	for(int i=1;i<=m;i++){
		kazuha+=g[i]*(m-i+1);
	}
	printf("%.3llf\n",kazuha);
	return 0;
}

后记

有的爱像阳光倾落,边拥有边失去着

题面

题目传送门

前言

期望 T5,应用题

正文

这道题的精髓就在于答案统计

提示:因为他是个环,所以要有一个很别扭的状态设计

dpi,0/1 表示长度为 i 的序列,第一个和第二个的颜色不同,第一个和最后一个的颜色不同/相同的期望

p 为有 i 个连续颜色相同的球的概率

转移:

dpi,0=pj×j×(dpij,0×m2m+dpij,1×m1m)

dpi,1=pj×j×dpij,0×1m

这个很好理解

然后就是怎么统计答案了

来再看看状态吧

第一个和第二个的颜色不同

为了避免容斥的状态

所以这里第一个球他就不是一个球,而是一个连续段,压缩成的一个球

设这个连续段的长度为 i

第一个球选这个连续段里的哪个都行,所以有 i

所以答案统计就为 i×i×pi×dpni+1,0

没有 dp1,0 这个状态(显然是矛盾的),所以答案要加上 n×pn

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=205;
int n,m;
long double f[N][2],g[N];
signed main(){
	cin>>n>>m;
	g[1]=1.0;
	for(int i=2;i<=n;i++)g[i]=g[i-1]/m;
	f[1][1]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			f[i][0]+=j*g[j]*(f[i-j][0]*(m-2)/m+f[i-j][1]*(m-1)/m);
			f[i][1]+=j*g[j]*f[i-j][0]/m;
		}
	}
	long double kazuha=g[n]*n;
	for(int i=1;i<n;i++)kazuha+=i*i*f[n-i+1][0]*g[i];
	printf("%.5llf",kazuha);
	return 0;
}

后记

好想看微信……
不敢看微信……

题面

题目传送门

前言

期望 T6,应用题

正文

每个按键都不可能被其他按键的组合键替代,按了其他的键就还要必须按一个相同的键给按回来,这样我们就可以dp了

fi 表示从 i 个需要按的键到 i1 个需要按的键的期望操作次数

这个状态就很神,他没有设最终的状态,他设的是一个过程量

转移也很好理解

fi=in+nin×(fi+fi+1+1)

就是有 in 的概率按到正确位置,nin 按到错误位置

按错了就多了一个未归位按键,需要一步把他按回来,按回来后还是有 i 个按键需要按

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e5+3;
int n,k,a[mod],f[mod],cnt;
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b%2)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=n;i>=1;i--){
        if(a[i]){
            cnt++;
            for(int j=1;j*j<=i;j++){
                if(i%j==0){
                    a[j]^=1;
                    if(j*j!=i)a[i/j]^=1;
                }
            }
        }
    }
    for(int i=n;i>=1;i--){
        f[i]=(n+(n-i)*f[i+1]%mod)%mod*qpow(i,mod-2)%mod;
    }
    int kazuha=k;
    if(cnt<=k)kazuha=cnt;
    else{
        for(int i=cnt;i>k;i--){
            kazuha=(kazuha+f[i])%mod;
        }
    }
    for(int i=1;i<=n;i++)kazuha=kazuha*i%mod;
    cout<<kazuha;
    return 0;
}

后记

这年头做个题都被刀
(注意到原题一句话:Zeit und Raum trennen dich und mich)

禁止使用翻译

题面

题目传送门

前言

期望 T7,大水题(本蒟蒻都能切)

正文

直观的

fi,j,k,l 表示打到 i 背包容量为 j 残片个数为 k 成功次数为 l 的概率

ai>0 fi,j,k,l=(1pi)fi1,j,k,l+pi×fi1,jai,k,l1

ai=1 fi,j,k,l=(1pi)fi1,j,k,l+pi×fi1,j,k1,l1

最后统计答案时就统计 j>=k 的就行了

时间复杂度 O(n4) 会爆

发现给 ai 分类讨论的形式好彪啊

因为 ai=1 就相当于给背包容量 1

最后统计答案就统计 j>=0 的就行了

还有一个问题是背包容量的上限

发现只要 j>=n 后面再怎么减也不会减成负数

于是 j>n 的就直接归到 j=n 的即可

时间复杂度 O(n3)

下表为负需要平移一下

代码

#include <bits/stdc++.h>
using namespace std;
const int N=205;
int n,l,K,a[N];
long double p[N],f[2][N<<1][N];
int main(){
    cin>>n>>l>>K;
    for(int i=1;i<=n;i++){
        cin>>p[i];
        p[i]/=100.0;
      //  cout<<p[i]<<" ";
    }   
    for(int i=1;i<=n;i++)cin>>a[i];
    K=min(n,K);
    f[0][n+K][0]=1;
    int d=0;
    for(int i=1;i<=n;i++){
        d^=1;
       // cout<<d<<endl;
        for(int k=0;k<=n;k++){
            for(int j=0;j<=n*2;j++){
                f[d][j][k]=(1-p[i])*f[d^1][j][k];
            }
        }
        for(int k=0;k<n;k++){
            for(int j=1;j<=n*2;j++){
                f[d][min(j+a[i],n*2)][k+1]+=p[i]*f[d^1][j][k];
            }
        }
    }
    long double kazuha=0;
    for(int i=n;i<=2*n;i++){
        for(int j=l;j<=n;j++){
            kazuha+=f[n&1][i][j];   
        }
    }
    printf("%.6llf",kazuha);
    return 0;
}

后记

或许,我爱上的不是她,而是我的想象力

posted @   小惰惰  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
/* 鼠标点击求赞文字特效 */
点击右上角即可分享
微信分享提示

目录导航