1.期望DP
洛谷P1365 WJMZBMR打osu! / Easy
题解
令
f
[
i
]
f[i]
f[i]表示到第i个字符的期望长度,
g
[
i
]
g[i]
g[i]表示以第i个字符为结尾的连续o串长度的期望,此时就有三种情况
s
[
i
]
=
=
o
时
,
f
[
i
]
=
f
[
i
−
1
]
+
(
g
[
i
−
1
]
+
1
)
2
−
g
[
i
−
1
]
2
=
f
[
i
−
1
]
+
2
∗
g
[
i
−
1
]
+
1
,
g
[
i
]
=
g
[
i
−
1
]
+
1
s[i] == o 时, f[i] = f[i - 1] + (g[i-1] + 1)^2 -g[i-1]^2 = f[i - 1] + 2 * g[i - 1] + 1,g[i] = g[i -1] + 1
s[i]==o时,f[i]=f[i−1]+(g[i−1]+1)2−g[i−1]2=f[i−1]+2∗g[i−1]+1,g[i]=g[i−1]+1
当
s
[
i
]
=
=
x
时
,
g
[
i
]
=
0
,
f
[
i
]
=
f
[
i
−
1
]
当s[i] == x时,g[i] = 0,f[i] = f[i - 1]
当s[i]==x时,g[i]=0,f[i]=f[i−1]
当
s
[
i
]
=
=
?
时
,
f
[
i
]
=
f
[
i
]
+
(
2
∗
g
[
i
−
1
]
+
1.0
)
∗
p
,
g
[
i
]
=
(
g
[
i
−
1
]
+
1
)
∗
p
(
p
为
?
为
o
的
概
率
,
这
题
是
0.5
)
当s[i] == ?时,f[i] = f[i] + (2 * g[i - 1] + 1.0) * p,g[i]=(g[i - 1] + 1)*p(p为?为o的概率,这题是0.5)
当s[i]==?时,f[i]=f[i]+(2∗g[i−1]+1.0)∗p,g[i]=(g[i−1]+1)∗p(p为?为o的概率,这题是0.5)
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
double f[N], g[N];
char s[N];
int main() {
int n;
cin >> n >> s + 1;
for(int i = 1; i <= n; i ++ ) {
if(s[i] == 'o') {
g[i] = g[i - 1] + 1.0;
f[i] = f[i - 1] + 2.0 * g[i - 1] + 1.0;
} else if(s[i] == 'x') {
g[i] = 0;
f[i] = f[i - 1];
} else if(s[i] == '?') {
g[i] = (g[i - 1] + 1.0) / 2.0;
f[i] = f[i - 1] + (2.0 * g[i - 1] + 1.0) / 2.0;
}
}
printf("%.4lf", f[n]);
return 0;
}
洛谷P1654 OSU!
题解
本题和上题差不多,不同的是本题需要维护长度 l e n len len和 l e n 2 len^2 len2的期望,注意不能只维护 l e n len len的期望,因为 E ( x 2 ) ≠ E ( x ) 2 E(x^2)\neq E(x)^2 E(x2)=E(x)2
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
double p;
double f[N]; // 到第i个的期望长度
double a[N], b[N]; // len的期望 和 len方的期望
double cal(double x) {
return x * x * x;
}
int main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int n;
cin >> n;
double res = 0;
double len = 0;
double p;
for(int i = 1; i <= n; i ++ ) {
cin >> p;
a[i] = (a[i - 1] + 1) * p;
b[i] = (b[i - 1] + (2.0 * a[i - 1] + 1.0)) * p;
f[i] = f[i - 1] + ((3.0 * b[i - 1] + 3.0 * a[i - 1] + 1.0)) * p;
}
printf("%.1lf", f[n]);
return 0;
}
2.推公式
P4562 [JXOI2018]游戏
题解
我们发现,对于一系列具有倍数关系数字2,4,6,8,检查时间其实是由他们种最小的数所决定的,以样例为例,将2,4分为一类,3分为一类,最后的检查时间是由2和3的位置所决定,所以可以枚举所有可能的时间,排列组合一下即可
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e7 + 5;
int fac[N], infac[N];
int pre[N];
bool vis[N];
int sz[N];
int qpow(int a, int n, int mod) {
int res = 1;
while(n) {
if(n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
void init(int n) { //预处理阶乘和逆元
fac[0] = 1;
infac[0] = 1;
for(int i = 1; i <= n; i ++ ) fac[i] = fac[i - 1] * i % mod;
infac[n] = qpow(fac[n], mod - 2, mod);
for(int i = n - 1; i >= 1; i -- ) {
infac[i] = infac[i + 1] * (i + 1) % mod;
}
//for(int i = 1; i <= 10; i ++ ) cout << fac[i] << " ";
//cout << endl;
//for(int j = 1; j <= 10; j ++ ) cout << infac[j] << " ";
//cout << endl;
}
inline int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
int C(int a, int b) {
return fac[a] % mod * infac[a - b] % mod * infac[b] % mod;
}
signed main() {
int l, r;
cin >> l >> r;
init(r + 1);
int n = r - l + 1;
int x = 0; // x为最小的数的个数
for(int i = l; i <= r; i ++ ) {
if(vis[i]) continue;
x ++ ;
for(int j = 2; j * i <= r; j ++ ) {
if(vis[i * j]) continue;
vis[i * j] = true;
//cout << i << " " << j << endl;
}
}
//for(int i = l; i <= r; i ++ ) cout << pre[i] << " ";
int res = 0;
//cout << C(5, 3) << endl;
//cout << x << endl;
for(int i = x; i <= n; i ++ ) { // 枚举可能的时间
res += x * C(n - x, i - x) % mod * fac[i - 1] % mod * fac[n - i] % mod * i % mod;
res %= mod;
}
// 对于时间是i的情况,说明x个数必须在前i个位置且有一个处于第i个位置,于是固定第i个位置,前面和后面全排列即可
cout << res << endl;
return 0;
}
P3802 小魔女帕琪
题解
如果我们能知道每七个连续的晶体释放魔法的概率,将所有的可能性加起来便是结果,令 a 1 , a 2 . . . a 7 为 每 种 属 性 的 晶 体 数 量 , a_1,a_2...a_7为每种属性的晶体数量, a1,a2...a7为每种属性的晶体数量,以前七个为例,如果能施放魔法,概率必为 7 ! × C n a 1 × C n − 1 a 2 . . . × C n − 6 a 7 7!\times C_n^{a1}\times C_{n-1}^{a2}...\times C_{n - 6}^{a7} 7!×Cna1×Cn−1a2...×Cn−6a7,7!是因为释放一次魔法时每种属性的晶体位置不确定,接下来我们可以感性的理解一下,对于抽签,在每次不知道前者结果时概率时一样的,本题也可以这么理解,之后每七个能释放魔法的概率均相同,由于长度为7的串一共有 n − 6 n-6 n−6个,最后答案乘上即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 10;
double f[N], g[N];
int a[N];
double res = 1;
int main() {
int n = 0;
for(int i = 1; i <= 7; i ++ ) {
cin >> a[i];
n += a[i];
}
int fac7 = 1 * 2 * 3 * 4 * 5 * 6 * 7;
bool flag = true;
for(int i = 1; i <= 7; i ++ ) {
if(n - i + 1 == 0) {
flag = false;
break;
}
res *= a[i] * 1.0 / (n - i + 1);
}
if(!flag) cout << "0.000" << endl;
else {
res = res * fac7 * (n - 6);
printf("%.3lf", res);
}
return 0;
}