4/8下午练习

4/8下午练习

四月 08, 2022
T1

来源: WinnieChen的考试

题意:

  两组人,第一组人的能力值分别为 $a_1,a_2,…,a_n$ 第二组人的能力值为 $b_1,b_2,…,b_n$ ,$a,b\in[1,2]$ 第一组和第二组的人两两比赛,能力值为 $x , y$ 进行比赛时能力值为 $x$ 的人获胜概率为 $\displaystyle\frac {x}{x+y}$ 。

  求第一组中每个玩家获胜场次的期望(要求误差 $10^{-3}​$)。

题解:

  发现对于每个 $a$ 求的其实是: $\displaystyle \sum\limits_{i=1}^{m} \dfrac{a}{a + b_i}$ 。发现并不好求,考虑转化一下,

  对其中的$\displaystyle \frac{b_i}{a+b_i}$ 进行泰勒展开($\displaystyle f(x) = \sum_{i=0}^{\infty } \frac{f^{(i)}{x_0}}{i!} (x - x_0)^i$)。

  泰勒展开的余项收敛条件:$\displaystyle \lim_{i\rightarrow\infty}\left( \frac{f^{(i)}{(x_0)}}{i!}(x - x_0)^i\right) = 0$ 的条件是 $x - x_0 < 1$ 。在本题中 $x\in[1,2]$ ,$x_0$ 取 1.5 这样在 $i$ 取到30左右的时候 $(x-x_0)^i$ 就已经小到可以忽略了。

  因此,我们考虑如何将$g(x)=\dfrac{b_i}{x+b_i}$在$x=1.5$处展开,也就是要求出在$i\in[0,30]$时的$g^{(i)}(x)$

  结论是$\displaystyle g^{(i)}(x)=(-1)^i\dfrac{b_i\times i!}{(x+b_i)^{i+1}}$

  证明:考虑使用数学归纳法

  显然$i=0$时原式符合条件,假设当$i=a$时满足条件,则只需证明$i=a+1$时满足条件即可

  由于有函数相除的求导公式$\left(\dfrac{f(x)}{g(x)}\right)’=\dfrac{f’(x)g(x)-f(x)g’(x)}{g^2(x)}$,则可以推出

  得证

  也就是说,根据泰勒展开,$\displaystyle g(x)=\sum_{i=0}^{\infty}(-1)^i\dfrac{b_i}{(x+b_i)^{(i+1)}}(x-x_0)^i$

  有上面得到本题的 $\infty$ 取30 即可。

  那么,对于$\sum g(x)$,$(x-x_0)^i$项前面的系数为每一个$g(x)$对应项系数的$\sum$

  所以,使用一个数组$d$,存储$\sum g(x)$每一项的系数,对于每一个$b$所对应的$g(x)$,求出在$i\in[0,30]$时的$(-1)^i\dfrac{b_i}{(x+b_i)^{(i+1)}}$,累加进$d[i]$,则$d$数组所代表的多项式即为$\displaystyle\sum_{i=1}^m\dfrac{b_i}{x+b_i}$

  接下来,遍历$a$数组,将每一个数当作$x$代入进多项式中(此处注意,多项式的自变量是$(x-x_0)$),用$m$减去所得的数即为该数答案

  时间复杂度$O(30(n+m))$

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* Written by Rikka__  */
/* ✧*。٩(ˊᗜˋ*)و✧*。 */
#include<bits/stdc++.h>
using namespace std;
#define _ =read()
inline int read(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
namespace FileIO{
string InFile;
string OutFile;
void File(string s){
InFile = s + ".in";
OutFile = s + ".out";
freopen(InFile.data() , "r" , stdin);
freopen(OutFile.data() , "w" , stdout);
}
}
const int Maxn = 1000100;
double a[Maxn];
double b[Maxn];
int n;
int m;
const int Maxk = 30;
const double delta = 1.5;
double d[Maxn];
int main(){
//FileIO::File("owo");
n _ , m _;
for(int i = 1 ; i <= n ; i++) scanf("%lf" , &a[i]);
for(int i = 1 ; i <= m ; i++) scanf("%lf" , &b[i]);
for(int i = 1 ; i <= m ; i++){
double res = b[i] / (b[i] + delta);
double j = -(b[i] + delta);
d[0] += res;
for(int k = 1 ; k <= Maxk ; k++) res /= j , d[k] += res;
}
for(int i = 1 ; i <= n ; i++){
double res = 1.0;
double j = (a[i] - delta);
double ans = m;
for(int k = 0 ; k <= Maxk ; k++) ans -= res * d[k] , res *= j;
printf("%.15f\n" , ans);
}
getchar();
return 0;
}


T2

来源:WinnieChen的考试

题意:

  $K$ 维空间随机游走,问 $n$ 步之后走过的不同位置的个数的期望。

题解:

  考虑对于每个格子会不会被经过。

  $f[i]$ 表示 $i$ 步之后第一次经过某个格子的方案。

  $g[i]$ 表示 $i$ 步之后在这个格子停下的方案。 

  $h[i]$ 表示从一个位置出发 $i$ 步回到原点的方案。

  有: $\displaystyle f[i] = g[i] - \sum_{j+k = i} f[j] * h[k]$

​ 考虑式子中的 $h[i]$ 与格子是哪个没有关系,所以式子是一个 $g \rightarrow f$ 的线性变换。

  对所有的格子求和,有 $\displaystyle dp[i] = (2k)^i - \sum_{j+k = i} dp[j] * h[k]$

  利用组合数直接求暴力卷积即可。

  考虑求$h[i]$ , 发现每一维度是独立的,直接进行背包就行。
Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* Written by Rikka__  */
/* ✧*。٩(ˊᗜˋ*)و✧*。 */
#include<bits/stdc++.h>
using namespace std;
#define _ =read()
inline int read(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
namespace FileIO{
string InFile;
string OutFile;
void File(string s){
InFile = s + ".in";
OutFile = s + ".out";
freopen(InFile.data() , "r" , stdin);
freopen(OutFile.data() , "w" , stdout);
}
}

const int Maxn = 2050;
int n;
int m;
int k;
int P;
int C[Maxn][Maxn];
int f[Maxn];
int g[Maxn];
int h[Maxn];
int ans;
int pw[Maxn];
signed main(){
//FileIO::File("qaq");
n _ , k _ , P _;
C[0][0] = 1;
for(int i = 1 ; i <= n ; i++){
C[i][0] = 1 , C[i][i] = 1;
for(int j = 1 ; j < i ; j++){
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
}
}
f[0] = 1, m = n >> 1;
for(int i = 1 ; i <= k ; i++){
for(int j = 0 ; j <= m ; j++) g[j] = f[j];
for(int j = 0 ; j <= m ; j++) f[j] = 0;
for(int j = 0 ; j <= m ; j++){
for(int o = 0 ; o <= m - j ; o++){
f[j + o] = (f[j + o] + 1ll * g[j] * C[j + o << 1][o << 1] % P * C[o << 1][o]) % P;
}
}
}
for(int i = 0 ; i <= m ; i++){
h[i] = f[i];
for(int j = 1 ; j < i ; j++){
h[i] = (h[i] - 1ll * h[j] * f[i - j] % P) % P;
h[i] = (h[i] + P) % P;
}
}
pw[0] = 1;
for(int i = 1 ; i <= n ; i++) pw[i] = 2ll * pw[i - 1] * k % P;
ans = 1ll * (n + 1) * pw[n] % P;
for(int i = 1 ; i <= m ; i++){
ans = (ans - 1ll * h[i] * pw[n - i * 2] % P * (n - 2 * i + 1) % P) % P;
if(ans < 0) ans += P;
}
cout << ans;
getchar();
return 0;
}


T3

来源:WinnieChen的考试

题意:

  给定一颗树,$q$ 次询问 $A$ 在 $ t_1$ 时刻从 $x$ 出发, $B$ 在 $t_2$ 时刻从 $y$ 出发,速度相同,问两人是否同时出现在一条边上(不包括顶点)。

题解:

  和之前考试的题比较像,那题求得是能否相遇。

  这题直接用 $LCA$ 求出路径的交,如果是同向的,比较一下时间差和路径上最长的边;如果是异向的,要判断会不会在端点相遇。

Code:

1
//咕掉了