概率、期望

浪子不回头ぞ 提交于 2021-01-03 07:59:19

BZOJ1076 奖励关

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。

在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。

宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。

获取第i种宝物将得到Pi分,但并不是每种宝物都是可以随意获取的。

第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。

注意,Pi可以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。

假设你采取最优策略,平均情况你一共能在奖励关得到多少分值?

1<=k<=100,1<=n<=15

正着做感觉不是很方便,所以考虑倒着做。

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=100+7,maxs=(1<<15)+7;
int n,k,d[maxn];
db v[maxn],dp[maxn][maxs];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(k); read(n); int x;
	For(i,1,n) {
		read(v[i]); read(x);
		while(x) {
			d[i]|=(1<<x-1);
			read(x);
		}
	}
	int sz=1<<n;
	Rep(i,k,1) {
		For(j,0,sz-1) {
			For(t,1,n) 
				if((j&d[t])==d[t]) dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<t-1)]+v[t]);
				else dp[i][j]+=dp[i+1][j];
			dp[i][j]/=n;
		}
	}
	printf("%.6f\n",dp[1][0]);
	return 0;
}

 

 

Uva 11201麻球繁衍

你有一坨K个毛球。这种毛球只会存活一天。

在死亡之前,一个毛球有P_i的概率生出i个毛球(i=0,1,…,n-1)。

m天后所有毛球都死亡的概率是多少?(包含在第m天前全部死亡的情况)

1<=n<=1000,0<=k<=1000,0<=m<=1000

dp[i]表示最初有一个毛球,最后再第i天没有毛球的概率。

计算dp[i+1]我们枚举第一天毛球的的繁衍量,如果是j,那么再过i天没有毛球的概率就是$dp[i]^j$

 

bzoj3450 Easy

某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(

我们来简化一下这个游戏的规则

有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有a*a分,comb就是极大的连续o。

比如ooxxxxooooxxx,分数就是2*2+4*4=4+16=20。

Sevenkplus闲的慌就看他打了一盘,有些地方跟运气无关要么是o要么是x,有些地方o或者x各有50%的可能性,用?号来表示。

比如oo?xx就是一个可能的输入。

那么WJMZBMR这场osu的期望得分是多少呢?

n<=300000

因为平方一类的不是一次的概率不是很好算,但是我们知道,假如原来有连续的i-1个,现在再多一个,多出来的代价就是$i^2 -(i-1)^2 = 2 \times i -1$

这个是一次的,所以我们先求出每个点连续o的个数的期望。剩下的就很好算了

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=3e5+7;
int n;
char s[maxn];
db ans[maxn],d[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(n); scanf("%s",s+1);
	For(i,1,n) {
		if(s[i]=='x') d[i]=0,ans[i]=ans[i-1];
		else if(s[i]=='o') d[i]=d[i-1]+1,ans[i]=ans[i-1]+2*d[i]-1;
		else {
			ans[i]=ans[i-1]+d[i-1]+0.5;
			d[i]=(d[i-1]+1)/2;
		}
	}
	printf("%.4f",ans[n]);
	return 0;
}

 

bzoj1426 收集邮票

有n种不同的邮票,皮皮想收集所有种类的邮票。

唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。

但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。

现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。

N<=10000

这竟然是第一个我可以自己做出来的概率期望题。虽然也很无脑。

令$f[i]$表示还剩i种邮票没有收集到,期望还需要买几张邮票

$f[i]= \frac{n-i}{n} \times f[i] + \frac{i}{n} \times f[i-1] +1$

解得$f[i]=f[i-1]+n/i$

令$dp[i]$表示有i种邮票没收集到,期望需要多少钱买邮票

$dp[i]= \frac{n-i}{n} \times (dp[i]+f[i]+1) + \frac{i}{n} \times (dp[i-1]+f[i-1]+1)$

解得$dp[i]=dp[i-1]+f[i-1]+ \frac{n-i}{i} \times f[i] +1 $

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e4+7;
int n;
db f[maxn],dp[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(n);
	For(i,1,n) f[i]=f[i-1]+(db)n/i;
	For(i,1,n) dp[i]=dp[i-1]+f[i-1]+(db)f[i]*(n-i)/i+(db)n/i;
	printf("%.2f\n",dp[n]);
	return 0;
}

  

bzoj3270 博物馆

Petya和他的朋友Vasya决定去参观一座城堡博物馆。

这座博物馆包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。

两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。

等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)

他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),

有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。

两个男孩同时行动。当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。

两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

n<=20

因为概率之间相互依赖形成环,所以需要高斯消元解方程,O(n^6)

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=400+7,maxm=maxn*maxn;
int n,m,S,T;db p[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

const db eps=1e-12,PI=acos(-1);
int get_id(int x,int y) {return (x-1)*n+y;}
int dcmp(db x){return fabs(x)<eps? 0:(x>0? 1:-1);}

int fir[maxn],nxt[2*maxm],to[2*maxm],e=0,ind[maxn];
void add(int x,int y) {
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
	to[++e]=x;nxt[e]=fir[y];fir[y]=e;
	++ind[x]; ++ind[y];
}

db D[maxn][maxn];
void Gauss() {
	int nn=n*n,pos=0;
	For(i,1,nn) {
		For(j,i,nn) if(dcmp(D[j][i])!=0) {pos=j; break;}
		if(i!=pos) swap(D[i],D[pos]);
		Rep(j,nn+1,i) D[i][j]/=D[i][i];
		For(j,i+1,nn) Rep(k,nn+1,i) D[j][k]-=D[j][i]*D[i][k];
	}
	Rep(i,nn,1) For(j,i+1,nn) if(dcmp(D[i][j])!=0) {
		D[i][n*n+1]-=D[i][j]*D[j][n*n+1];
	}
}

int main() {
	read(n); read(m); read(S); read(T);
	int x,y;
	For(i,1,m) {
		read(x); read(y);
		add(x,y);
	}
	For(i,1,n) scanf("%lf",&p[i]); 
	int id;db P;
	For(i,1,n) For(j,1,n) if(i!=j) {
		id=get_id(i,j);
		D[id][id]=1-p[i]*p[j];
		for(x=fir[i];x;x=nxt[x]) D[get_id(to[x],j)][id]-=p[j]*(1-p[i])/ind[i];
		for(y=fir[j];y;y=nxt[y]) D[get_id(i,to[y])][id]-=p[i]*(1-p[j])/ind[j];
		P=(1-p[i])*(1-p[j])/ind[i]/ind[j];
		for(x=fir[i];x;x=nxt[x]) for(y=fir[j];y;y=nxt[y]) 
			D[get_id(to[x],to[y])][id]-=P;
	}
	else {
		id=get_id(i,i);
		D[id][id]=1;
	}
	D[get_id(S,T)][n*n+1]=1;
	Gauss();
	For(i,1,n) printf("%.6f ",D[get_id(i,i)][n*n+1]);
	return 0;
}

  

bzoj4872 分手是祝愿

B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。
n<=1e5
首先解是唯一的,我们可以用dp[i]表示有i个和解不对的开关的状态还需要多少步
$dp[i]=\frac{i}{n}\times dp[i-1] + \frac{n-i}{n} \times dp[i+1] +1$
$\frac{i}{n} \times (dp[i]-dp[i-1]) = \frac{n-i}{n} \times (dp[i+1]-dp[i]) +1$
令$g[i]=dp[i]-dp[i-1]$
$\frac{i}{n} \times g[i] = \frac{n-i}{n} \times g[i+1] +1$
$g[i]= \frac{n-i}{i} g[i+1]+\frac{n}{i}$
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e5+7;
const ll mod=100003;
ll n,k,a[maxn],f[maxn],last,tot,g[maxn],inv[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(n); read(k);
	For(i,1,n) read(a[i]);
	Rep(i,n,1) {
		for(int j=i;j<=n;j+=i) if(f[j]) a[i]^=1;
		if(a[i]) { ++tot; f[i]=1; }
	}
	if(tot<=k) {
		For(i,2,n) tot=tot*i%mod;
		printf("%lld",tot);
	}
	else {
		g[n]=inv[1]=1;
		For(i,2,n) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		Rep(i,n-1,k+1) g[i]=((n-i)*g[i+1]+n)%mod*inv[i]%mod;
		ll ans=0;
		For(i,k+1,tot) ans+=g[i];
		ans+=k; ans%=mod;
		For(i,2,n) ans=ans*i%mod;
		printf("%lld",ans);
	}
	return 0;
}

  

bzoj5197

给定一张n个点,m条双向边的无向图。 你要从1号点走到n号点。当你位于x点时,你需要花1元钱,等概率随机地买到与x相邻的一个点的票,只有通过票才能走到其它点。
每当完成一次交易时,你可以选择直接使用那张票,也可以选择扔掉那张票然后再花1元钱随机买另一张票。注意你可以无限次扔票。
请使用最佳的策略,使得期望花的钱数最少。
1<=n,m<=300000
dyx学长推荐的题。
G题
考虑如果用dp[i]表示从i走到n的期望花费,那么在所有与i相连的j中满足dp[j]<dp[i]的才对dp[i]有贡献。
所以考虑从n开始,dp[n]=0,更新其他的dp值,就像最短路一样,可以用堆优Dijkstra。
没有写
 
bzoj4008亚瑟王
玩家有一套卡牌,共 n张。游戏时,玩家将 n 张卡牌排列成某种顺序,排列后将卡牌按从前往后依次编号为 1 ~  n。本题中,顺序已经确定,即为输入的顺序。
每张卡牌都有一个技能。第 i 张卡牌的技能发动概率为 pi,如果成功发动,则会对敌方造成di点伤害。也只有通过发动技能,卡牌才能对敌方造成伤害。
 一局游戏一共有 r 轮。在每一轮中,系统将从第一张卡牌开始,按照顺序依次考虑每张卡牌。在一轮中,对于依次考虑的每一张卡牌: 
1如果这张卡牌在这一局游戏中已经发动过技能,则 
1.1 如果这张卡牌不是最后一张,则跳过之(考虑下一张卡牌); 否则(是最后一张),结束这一轮游戏。 
2否则(这张卡牌在这一局游戏中没有发动过技能),设这张卡牌为第 i 张 
2.1将其以 pi的概率发动技能。 
2.2如果技能发动,则对敌方造成 di点伤害,并结束这一轮。 
2.3如果这张卡牌已经是最后一张(即 i 等于n),则结束这一轮;否则,考虑下一张卡牌。 
请帮助小 K 求出这一套卡牌在一局游戏中能造成的伤害的期望值。 
1 <= T <= 444, 1 <= n <= 220, 0 <= r <= 132, 0 < pi < 1, 0 <= di <= 1000
此题害人不浅,让我想了一上午都没想出来
难点在两个地方:

1.若一张牌发动了技能,那么将结束此回合。

2.若一张牌发动过技能那么它不能再发动技能。

所以,如果我们总是在纠结到底是哪轮游戏发动了第i个技能的话,太困难了。
你会发现,无论怎么dp,你尝试用乘法原理都是错的,因为前后总是有关联的,不能直接乘。(不要试了,我尝试了一上午呢)
这道题,最重要的一点是,我们只需要知道发动一个技能的概率,不必要去算什么,他是第几轮游戏发动的,前面有几轮曾经有机会发动但是没发动。
我们只需要知道,如果一共有x个机会发动,他到底有没有发动,因为没发动的概率很好算,就是(1-p[i])^x,所以反过来算发动的概率1-(1-p[i])^x,就不需要枚举是第几次机会发动的了
而且这个东西是可以转移的,就是说如果i有x个机会发动,那么i-1至少有x个机会发动。
或者反过来如果i有x个机会发动,那么如果他发动一次,i+1有x-1的机会发动,如果他不发动,i+1有x个机会发动。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=220+7;
int Td,n,m;
db f[maxn][maxn],g[maxn],p[maxn][maxn],d[maxn],ans;

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

const db eps=1e-15;
int dcmp(db x) {return fabs(x)<eps? 0:(x>0? 1:-1);}

int main() {
	read(Td);
	while(Td--) {
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		read(n); read(m); ans=0;
		For(i,1,n) {
			scanf("%lf%lf",&p[i][1],&d[i]);
			p[i][1]=1-p[i][1];
			p[i][0]=1;
			For(j,2,m) p[i][j]=p[i][j-1]*p[i][1];
		}
		For(i,0,m) p[0][i]=1;
		f[0][m]=1;
		For(i,0,n-1) For(j,0,m) if(dcmp(f[i][j])) {
			//not choose
			f[i+1][j]+=f[i][j]*p[i][j];
			//choose one
			if(j) f[i+1][j-1]+=f[i][j]*(1-p[i][j]);
		}
		For(i,1,n) {
			For(j,1,m) g[i]+=f[i][j]*(1-p[i][j]);
			ans+=g[i]*d[i];
		}
		printf("%.10f\n",ans);
	} 
	return 0;
}

 

bzoj2438杀人游戏
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
1≤N ≤  10 0000,0≤M ≤  30 0000
细节题。
容易想到缩点然后取所有入度为0的点。
但是,,,如果我们知道了n-1个人都是平民,那我们是不是可以直接知道最后一个人是杀手了呢。
所以,如果存在一个入度为0的(缩过的)点,他只包含一个(缩过前的)点,并且他的所有出边所到的点,都可以由其它入度为0的点走到,那么我们就可以不取他。
为了方便判断是否存在这样的点:
那么我们在scc间连边的时候,我们至少要保证,不能从一个(缩过前的)点i所在scc x给另一个scc y连多条边,因为边连重了,就不好判断y可以由几个入度为0的点走到了,但是如果x含有多个点,不只是i,那么就无所谓了。
那个叫tp的函数不是拓扑排序。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=3e5+7,maxm=1e6+7;
int n,m,ans,p[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int fir[maxn],nxt[maxm],to[maxm],e=0;
void add(int x,int y) {
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
}

int FIR[maxn],NXT[maxm],TO[maxm],E=0,ind[maxn];
void ADD(int x,int y) {
	TO[++E]=y;NXT[E]=FIR[x];FIR[x]=E;
	++ind[y];
}

int dfn[maxn],low[maxn],dfn_clock;
int size[maxn],bel[maxn],toth,zz[maxn],t;
bool inz[maxn];
void tj(int pos) {
	dfn[pos]=low[pos]=++dfn_clock;
	zz[++t]=pos; inz[pos]=1; int y,z;
	for(y=fir[pos];y;y=nxt[y]) {
		if(dfn[z=to[y]]) {
			if(inz[z]) low[pos]=min(low[pos],dfn[z]);
			continue;
		}
		tj(z);
		low[pos]=min(low[pos],low[z]);
	}
	if(low[pos]==dfn[pos]) {
		++toth;
		do {
			bel[zz[t]]=toth;
			inz[zz[t]]=0;
			size[toth]++;
		}while(zz[t--]!=pos);
	}
}

void tp() {
	int s=0,t=0,x,y,z;
	For(i,1,toth) if(!ind[i]) zz[++t]=i;
	ans=t;
	For(i,1,t) if(size[x=zz[i]]==1) {
		for(y=FIR[x];y;y=NXT[y]) {
			if(ind[z=TO[y]]<2) {
				s=i; break;
			}
		}
		if(s!=i) {ans--;return;}
	}
}

int main() {
	read(n); read(m);
	int x,y,z;
	For(i,1,m) {
		read(x); read(y);
		add(x,y);
	}
	For(i,1,n) if(!dfn[i]) tj(i);
	For(i,1,n) {
		for(y=fir[i];y;y=nxt[y]) {
			if(bel[z=to[y]]==bel[i]||p[bel[z]]==i) continue;
			ADD(bel[i],bel[z]); p[bel[z]]=i;
		}
	}
	tp();
	printf("%.6f\n",(db)(n-ans)/(db)n);
	return 0;
}

 

bzoj1419 Red is good

桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付出1美元。可以随时停止翻牌,在最优策略下平均能得到多少钱。
R,B<=5000
水题。注意空间64M,需要滚动数组。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(register int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
const int maxn=5000+7;
int n,m,p=0;
db dp[2][maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(n); read(m);
	For(i,0,n) {
		p^=1;
		For(j,0,m) {
			dp[p][j]=0;
			if(i) dp[p][j]+=(dp[p^1][j]+1)*(db)i/(i+j);
			if(j) dp[p][j]+=(dp[p][j-1]-1)*(db)j/(i+j);
			dp[p][j]=max(dp[p][j],(db)0);
		}
	}
	printf("%.6f\n",dp[p][m]-0.0000005);
	return 0;
}

 

bzoj3143游走

一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。

当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

n<=500

直接高斯消元,一开始数组没开够WA了很久找不到问题。

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=500+7,maxm=2e6+7;
int n,m;
db D[maxn][maxn],P[maxm],g[maxm],ind[maxn],ans;

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

const db eps=1e-14;
int dcmp(db x) {return fabs(x)<eps? 0:(x>0? 1:-1);}

struct Edge{
	int x,y;
}edge[maxm];

void Guess() {
	db r;
	For(i,1,n) {
		For(j,i,n) if(dcmp(D[j][i])) {
			if(i!=j) swap(D[i],D[j]);
			break;
		}
		For(j,i+1,n) {
			r=D[j][i]/D[i][i];
			For(k,i,n+1) D[j][k]-=D[i][k]*r;
		}
	}
	Rep(i,n,1) {
		For(j,i+1,n) if(dcmp(D[i][j])) 
			D[i][n+1]-=D[i][j]*P[j];
		P[i]=D[i][n+1]/D[i][i];
	}
}

int main() {
	read(n); read(m); int x,y;
	For(i,1,n) D[i][i]=1; D[1][n+1]=1;
	For(i,1,m) {
		read(x); read(y);
		edge[i].x=x; edge[i].y=y;
		++ind[x]; ++ind[y];
		if(x!=n&&y!=n) --D[x][y],--D[y][x];
	}
	For(i,1,n) For(j,1,n) if(i!=j) D[i][j]/=ind[j];
	Guess();
	For(i,1,m) {
		x=edge[i].x; y=edge[i].y;
		g[i]+=P[x]/ind[x];
		g[i]+=P[y]/ind[y];
	}
	sort(g+1,g+m+1);
	For(i,1,m) ans+=g[i]*(m-i+1);
	printf("%.3f\n",ans);
	return 0;
}

 

bzoj4318 OSU

osu 是一款群众喜闻乐见的休闲软件。 
我们可以把osu的规则简化与改编成以下的样子: 
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。
在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释) 

现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。

N<=100000
无脑的dp
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e5+7;
int n;
db p[maxn],X[maxn],Y[maxn],Z[maxn],ans;

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int main() {
	read(n);
	For(i,1,n) scanf("%lf",&p[i]);
	For(i,1,n) X[i]=(X[i-1]+1)*p[i];
	For(i,1,n) Y[i]=(Y[i-1]+2*X[i-1]+1)*p[i];
	For(i,1,n) Z[i]=(Z[i-1]+3*Y[i-1]+3*X[i-1]+1)*p[i];
	For(i,1,n) ans+=Z[i]*(1-p[i+1]);
	printf("%.1f\n",ans);
	return 0;
}

 

bzoj1778驱逐猪猡

奶牛们建立了一个随机化的臭气炸弹来驱逐猪猡。猪猡的文明包含1到N (2 <= N <= 300)一共N个猪城。
这些城市由M (1 <= M <= 44,850)条由两个不同端点A_j和B_j (1 <= A_j<= N; 1 <= B_j <= N)表示的双向道路连接。
保证城市1至少连接一个其它的城市。
一开始臭气弹会被放在城市1。每个小时(包括第一个小时),它有P/Q (1 <= P <=1,000,000; 1 <= Q <= 1,000,000)的概率污染它所在的城市(爆炸)。
如果这个小时内它没有污染它所在的城市(爆炸),那麽它随机地选择一条道路,在这个小时内沿着这条道路走到一个新的城市。
可以离开这个城市的所有道路被选择的概率均等。
给定一个猪猡文明的地图和臭气弹在每个小时内爆炸的概率。计算每个城市最终被污染的概率。
高斯消元算出每个点在爆炸前访问到的次数的期望,然后用i的次数除以所有点次数期望和就是在i爆炸的概率。
友情提示:此题只能输出9位小数,而且有可能卡你精度。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db long double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=300+7,maxm=1e5+7;
int n,m;
db P,Q,D[maxn][maxn],CS[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int fir[maxn],nxt[2*maxm],to[2*maxm],e=0; db ind[maxn];
void add(int x,int y) {
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
	to[++e]=x;nxt[e]=fir[y];fir[y]=e;
	++ind[x]; ++ind[y];
}

const db eps=1e-15;
//int dcmp(db x){return fabs(x)<eps? 0:(x>0? 1:-1);}

void Guess() {
	db r;int p;
    For(i,1,n) {
    	p=i;
        For(j,i,n) if(fabs(D[j][i])>fabs(D[p][i])) p=j;
        if(i!=p) swap(D[i],D[p]);
        For(j,i+1,n) {
            r=D[j][i]/D[i][i];
            For(k,i,n+1) D[j][k]-=D[i][k]*r;
        }
    }
    Rep(i,n,1) {
        For(j,i+1,n) D[i][n+1]-=D[i][j]*CS[j];
        CS[i]=D[i][n+1]/D[i][i];
    }
}

int main() {
	read(n); read(m); read(P); read(Q);
	P/=Q; Q=1.0-P;
	int x,y,z;
	For(i,1,m) {
		read(x); read(y);
		add(x,y);
	}
	D[1][n+1]=1;
	For(i,1,n) {
		D[i][i]=1;
		for(y=fir[i];y;y=nxt[y]) {
			D[i][z=to[y]]-=Q/ind[z];
		}
	}
	Guess();
	For(i,1,n) CS[0]+=CS[i];
	For(i,1,n) printf("%.9Lf\n",fabs(CS[i]/CS[0]+eps));
	return 0;
}

 

BZOJ1444有趣的游戏

0<=P, n , l, m≤ 10.

注意,题意是,只要游戏没结束,时间一直延长。
明显的AC自动机+高斯消元,注意精度。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db long double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=500+7,maxt=23,maxm=2*maxn*maxt;
const db eps=1e-12;
int n,l,m;
db P[maxn],D[maxn][maxn],ans[maxn];
char s[maxn];
int son[maxn][maxt],fail[maxn],tot,num[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int fir[maxn],nxt[maxm],to[maxm],e=0;db p[maxm];
void add(int x,int y,db z) {
	x++; y++;
//	printf("add: %d->%d , %.2Lf\n",x,y,z);
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;p[e]=z;
}

void insert(int p) {
	int now=0,x;
	For(i,1,l) {
		x=s[i]-'A'+1;
		if(!son[now][x]) son[now][x]=++tot;
		now=son[now][x];
	}
	num[now+1]=p;
}

int zz[maxn];
void bld() {
	int s=1,t=0,x,y,z;
	For(i,1,m) if(son[0][i]) add(0,son[0][i],P[i]),zz[++t]=son[0][i];
	while(s<=t) {
		x=zz[s++];
		For(i,1,m) {
			if(son[x][i]) zz[++t]=son[x][i],fail[son[x][i]]=son[fail[x]][i];
			else son[x][i]=son[fail[x]][i];
			add(x,son[x][i],P[i]);
		}
	}
}

void Guess(int n) {
	db r;int p;
    For(i,1,n) {
        p=i;
        For(j,i,n) if(fabs(D[j][i])>fabs(D[p][i])) p=j;
        if(i!=p) swap(D[i],D[p]);
        if(fabs(D[i][i])<eps) continue;
        For(j,i+1,n) {
            r=D[j][i]/D[i][i];
            For(k,i,n+1) D[j][k]-=D[i][k]*r;
        }
    }
    Rep(i,n,1) {
        For(j,i+1,n) D[i][n+1]-=D[i][j]*P[j];
		if(D[i][i]) P[i]=D[i][n+1]/D[i][i];
	}
}

int main() {
	read(n); read(l); read(m);
	int x,y,z;
	For(i,1,m) {
		read(x); read(y);
		P[i]=(db)x/(db)y;
	}
	For(i,1,n) {
		scanf("%s",s+1);
		insert(i);
	}
	bld(); tot++;
	For(i,1,tot) {
		D[i][i]=1;
		if(!num[i]) for(y=fir[i];y;y=nxt[y]) D[z=to[y]][i]-=p[y];
	}
	D[1][tot+1]=1;
	For(i,1,tot) {
		if(num[i]) D[1][i]=1;
		else D[1][i]=0;
	}
	Guess(tot);
	For(i,1,tot) if(num[i]) ans[num[i]]=P[i];
	For(i,1,n) printf("%.2Lf\n",fabs(ans[i]));
	return 0;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!