5道不错的概率dp.
235B Let’s Play Osu!
你
打
音
游
的
得
分
是
你
每
次
击
中
连
续
音
符
个
数
的
平
方
和
.
给
出
你
击
中
每
一
个
音
符
的
概
率
,
求
你
的
期
望
得
分
.
你打音游的得分是你每次击中连续音符个数的平方和.\newline 给出你击中每一个音符的概率,求你的期望得分.
你打音游的得分是你每次击中连续音符个数的平方和.给出你击中每一个音符的概率,求你的期望得分.
考虑总贡献是两个击中音符的对数加上击中音符的个数.
每一个音符能够对两个击中音符的贡献
d
p
[
i
]
=
(
d
p
[
i
−
1
]
+
1
)
×
p
[
i
]
dp[i]=(dp[i-1]+1)\times p[i]
dp[i]=(dp[i−1]+1)×p[i].
前
i
i
i个音符能够对得分的贡献
s
u
m
[
i
]
=
(
s
u
m
[
i
−
1
]
+
d
p
[
i
]
×
2
+
1
)
×
p
[
i
]
sum[i]=(sum[i-1]+dp[i]\times2+1)\times p[i]
sum[i]=(sum[i−1]+dp[i]×2+1)×p[i].
最后的答案就是
s
u
m
[
n
]
sum[n]
sum[n].
#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int yuzu=1e5;
typedef double db;
int main() {
int i,n=read();
db llx=0,zxy=0,x;
for (i=1;i<=n;++i)
scanf("%lf",&x),
llx+=x*(1+zxy*2),
zxy=x*(zxy+1);
printf("%.12lf\n",llx);
}
351B Jeff and Furik
一
个
排
列
,
每
次
逆
序
对
个
数
先
减
少
一
,
然
后
以
1
2
的
几
率
增
加
一
或
者
减
少
1.
求
到
达
逆
序
对
个
数
为
0
的
期
望
次
数
.
一个排列,每次逆序对个数先减少一,然后以\frac{1}{2}的几率增加一或者减少1.\newline 求到达逆序对个数为0的期望次数.
一个排列,每次逆序对个数先减少一,然后以21的几率增加一或者减少1.求到达逆序对个数为0的期望次数.
结论题.每两次操作的逆序对期望减少个数等于
1
1
1.
答案就是逆序对的个数乘以
2
2
2.
注意如果逆序对有奇数个,在最后一次操作会直接排序完毕,因此要减去1.
const int aoi=3058;
int a[aoi];
int main() {
int i,j,n=read(),ans=0;
for (i=1;i<=n;++i)
for (a[i]=read(),j=1;j<i;++j)
ans+=a[j]>a[i];
write(ans*2-ans%2);
}
768D Jon and Orbs
有
k
种
物
品
,
每
次
等
概
率
获
得
其
中
一
种
,
求
获
得
全
部
物
品
的
概
率
大
于
等
于
p
−
1
0
−
7
2000
的
期
望
次
数
.
有k种物品,每次等概率获得其中一种,求获得全部物品的概率大于等于\frac{p-10^{-7}}{2000}的期望次数.
有k种物品,每次等概率获得其中一种,求获得全部物品的概率大于等于2000p−10−7的期望次数.
暴力进行期望
d
p
dp
dp求出操作
j
j
j次获得
i
i
i种物品的概率.
然后暴力跑每一组询问,答案最大是
7000
7000
7000多的一个数.
不行的话二分吧,也不难写.
const int aoi=1058;
typedef double db;
const db eps=1e-7;
db dp[aoi*8][aoi];
/*dp[i][j]表示取i次球,获得j种的概率.*/
int main() {
int k,q,i,j;
read(k),read(q);
for (**dp=i=1;i<=7274;++i) { //答案最大7274
for (j=1;j<=k;++j) {
dp[i][j]=dp[i-1][j]*j/k+dp[i-1][j-1]*(k-j+1)/k;
}
}
for (;q--;) {
db p=(read()*1.0-eps)/2000;
for (i=1;i<=7274&&dp[i][k]<p;++i);
write(i),pl;
}
}
50D Bombing
给
出
n
座
城
市
和
核
弹
爆
炸
点
,
给
出
核
弹
爆
炸
半
径
和
建
筑
物
离
爆
炸
点
距
离
与
建
筑
物
被
毁
概
率
的
关
系
,
求
想
要
至
少
炸
掉
k
座
城
市
失
败
的
概
率
小
于
p
核
弹
的
最
小
爆
炸
半
径
.
给出n座城市和核弹爆炸点,给出核弹爆炸半径和建筑物离爆炸点距离\newline 与建筑物被毁概率的关系,求想要至少炸掉k座城市失败的概率小于p核弹的最小爆炸半径.
给出n座城市和核弹爆炸点,给出核弹爆炸半径和建筑物离爆炸点距离与建筑物被毁概率的关系,求想要至少炸掉k座城市失败的概率小于p核弹的最小爆炸半径.
显然有单调性,二分答案.
对于二分出的答案,轻松进行期望
d
p
dp
dp,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i座城市被炸掉
j
j
j座的概率.
最后被炸掉
0
→
k
−
1
0\to k-1
0→k−1座都是失败的,把这些概率加起来与给定失败概率比较.
这概率公式牛逼.
const int z=233;
typedef double db;
const db e=2.7182818284590452353602874713527;
int n,x[z],y[z],cx,cy,k;
db fail,dp[z][z];
db dis(int a,int b,int k) {return sqrt((a-x[k])*(a-x[k])+(b-y[k])*(b-y[k]));}
bool akioi(db br) {
db t; int i,j;
memset(dp,0,sizeof dp);
for (**dp=i=1;i<=n;++i)
for (dp[i][0]=dp[i-1][0]*((t=dis(cx,cy,i))<=br?0:(1-pow(e,1-t*t/br/br))),j=1;j<=n;++j)
dp[i][j]=(t=dis(cx,cy,i))<=br?dp[i-1][j-1]:dp[i-1][j]*(1-pow(e,1-t*t/br/br))+dp[i-1][j-1]*pow(e,1-t*t/br/br);
db llx=0;
for (i=0;i<k;++i) llx+=dp[n][i];
return llx>fail;
}
int main() {
read(n); int i,t;
k=read(),fail=1.0*read()/1000;
cx=read(),cy=read();
for (i=1;i<=n;++i) x[i]=read(),y[i]=read();
db l=0,r=1e5,mid;
for (t=100;t--;akioi(mid)?l=mid:r=mid) mid=(l+r)/2;
printf("%.12lf\n",r);
}
101D Castle
你
在
树
的
1
号
节
点
,
树
上
每
条
边
有
经
过
的
时
间
,
而
且
最
多
只
能
经
过
两
次
.
树
上
除
了
1
号
节
点
每
一
个
节
点
都
可
能
有
宝
物
,
你
走
到
有
宝
物
的
节
点
会
停
下
来
.
求
你
获
得
宝
藏
走
的
距
离
的
期
望
.
你在树的1号节点,树上每条边有经过的时间,而且最多只能经过两次.\newline 树上除了1号节点每一个节点都可能有宝物,你走到有宝物的节点会停下来.\newline 求你获得宝藏走的距离的期望.
你在树的1号节点,树上每条边有经过的时间,而且最多只能经过两次.树上除了1号节点每一个节点都可能有宝物,你走到有宝物的节点会停下来.求你获得宝藏走的距离的期望.
首先这题不是
d
p
dp
dp.不是
d
p
!
dp!
dp!
我们需要考虑的是当前如果走到
u
u
u的子树,我们接下来应该怎么走是最优的.
由于时间原因,不给出证明了,可以直接看题解.
就是对
c
o
s
t
(
f
a
[
u
]
,
u
)
+
l
e
n
[
u
]
s
z
[
u
]
\frac{cost(fa[u],u)+len[u]}{sz[u]}
sz[u]cost(fa[u],u)+len[u]即
u
u
u到父亲的距离加上
u
u
u子树里所有的边权和除以
u
u
u子树的大小从小到大排序.
最后直接考虑每一个子树的贡献.
#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rel register ll
#define rec register char
#define gc getchar
//#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?-1:*p1++)
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
char buf[1<<23],*p1=buf,*p2=buf;
inline int read(){
int x=0,f=1;char c=gc();
for (;!isdigit(c);c=gc()) f^=c=='-';
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return f?x:-x;
}
template <typename mitsuha>
inline bool read(mitsuha &x){
x=0;int f=1;char c=gc();
for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
if (!~c) return 0;
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return x=f?x:-x,1;
}
template <typename mitsuha>
inline int write(mitsuha x){
if (!x) return 0&pc(48);
if (x<0) pc('-'),x=-x;
int bit[20],i,p=0;
for (;x;x/=10) bit[++p]=x%10;
for (i=p;i;--i) pc(bit[i]+48);
return 0;
}
inline char fuhao(){
char c=gc();
for (;isspace(c);c=gc());
return c;
}
}using namespace chtholly;
using namespace std;
const int yuzu=1e5;
typedef ll fuko[yuzu|10];
fuko sz,dp,len;
/*sz表示子树大小,len表示这个点到它父亲长度的两倍加上它整棵子树的边之和.dp表示这一个点到它的子树获得宝物所用时间的期望.*/
struct edge{int v,c;};
vector<edge> lj[yuzu|10];
void dfs(int u,int fa) {
vector<int> stk; sz[u]=1;
for (edge i:lj[u]) {
int v=i.v,c=i.c;
if (v^fa) {
dfs(v,u),sz[u]+=sz[v],len[u]+=(len[v]+=(c<<1));
dp[u]+=dp[v]+sz[v]*c,stk.push_back(v);
/*v子树走过的每一个节点都要算上从u到v这条边的长.*/
}
}
sort(stk.begin(),stk.end(),[](int a,int b){return sz[a]*len[b]>sz[b]*len[a];});
ll llx=0;
for (int v:stk) dp[u]+=sz[v]*llx,llx+=len[v];
/*走过前面的所有子树用掉的时间和*/
}
int main() {
int i,n,u,v,c;
for (read(n),i=1;i<n;++i) {
read(u),read(v),read(c),
lj[u].push_back(edge{v,c}),
lj[v].push_back(edge{u,c});
}
dfs(1,0);
printf("%.12lf\n",1.0*dp[1]/(n-1));
}
/*
在走到一个点的时候,我们需要确定先走哪一颗子树获得宝藏的概率最大.
这是一个贪心.
我们把子树按照子树里的所有边长度(包括父节点连到子树里的那一条边)/子树大小从小到大排序.
*/
谢谢大家.