牛客NowCoder OI周赛普及组14题解
A. String
题目
开一个数组直接统计。
#include <iostream>
#include <cstring>
using namespace std;
int a[300];
string s;
int main() {
cin >> s ;
int cnt = 0;
for (int i = 0; i < s.length(); i++) {
if (a[s[i]]++ == 0) cnt++;
}
cout << cnt << endl;
return 0;
}
B.Number
题目
模拟,但是提前打一个表,这样可以快速得到任何一个数的n次幂,防止超时。
#include <iostream>
using namespace std;
typedef long long ll;
ll pow[10][30];
bool check(ll x) {
for (int j = 1; j <= 21; j++) {
ll sum = 0, tmp = x;
while(tmp) {
sum += pow[tmp%10][j];
tmp /= 10;
}
if (sum == x) return true;
else if (sum > x) return false;
}
return false;
}
int main() {
for (int i = 1; i <= 9; i++) {
pow[i][1] = i;
for (int j = 2; j <= 21; j++)
pow[i][j] = pow[i][j-1] * i;
}
int n; cin >> n;
ll v; int cnt = 0;
for (int i = 0; i < n; i++) {
cin >> v;
if (check(v)) cnt++;
}
cout << cnt << endl;
return 0;
}
C.Tree
题目
思路
树形dp,换根。首先把无根树变成有根树,不妨选择1作为根,由此求出两个值(1)
s
i
z
[
v
]
siz[v]
siz[v] 和(2)
d
e
p
[
v
]
dep[v]
dep[v],分别代表以
v
v
v为根节点的子树的尺寸和深度和。
接下来考虑换根的状态转移。
令
f
[
v
]
f[v]
f[v]为以
v
v
v为根的价值总和,那么让根往他的子节点
u
u
u去转移,则可得到方程如下:
f
[
u
]
=
f
[
v
]
+
n
−
s
i
z
[
u
]
−
s
i
z
[
u
]
f[u]=f[v]+n-siz[u]-siz[u]
f[u]=f[v]+n−siz[u]−siz[u]
即
f
[
u
]
=
f
[
v
]
+
n
−
2
∗
s
i
z
[
u
]
f[u] =f[v]+n-2*siz[u]
f[u]=f[v]+n−2∗siz[u]
(对于所有处
u
u
u子树的节点,距离加1,所有在其子树上的节点,距离减1)
递归的去求得
f
f
f数组。最后找到最大值即可。
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int mxN = 1e6 + 10;
int siz[mxN], dep[mxN], f[mxN];
vector<int> G[mxN];
int n, ans;
void dfs(int v, int p) {
dep[v] = dep[p] + 1;
siz[v] = 1;
for (auto u : G[v]) {
if (u == p) continue;
dfs(u, v);
siz[v] += siz[u];
}
}
void solve(int v, int p) {
for (auto u : G[v]) {
if (u == p) continue;
f[u] = f[v] + n - 2 * siz[u];
solve(u, v);
}
}
int main() {
//cin >> n;
scanf("%d", &n);
for (int i = 0; i < n-1; i++) {
int a, b;
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
dep[0] = -1;
dfs(1, 0);
for (int i = 1; i <= n; i++) f[1] += dep[i];
solve(1, 0);
ans = 1 << 30;
for (int i = 1; i <= n; i++) ans = min(ans, f[i]);
//cout << ans << endl;
printf("%d\n", ans);
return 0;
}
Talk
题目
思路
随机游走模型,概率期望dp,手动解方程。
我们设
f
i
f_i
fi为从位置
i
i
i到达最终位置的期望步数。可得转移方程如下:
f
i
=
p
∗
f
i
+
1
+
(
1
−
p
)
∗
f
i
−
1
(
1
)
f_i=p*f_{i+1}+(1-p)*f_{i-1}(1)
fi=p∗fi+1+(1−p)∗fi−1(1),这是没办法直接通过动态规划进行状态转移的,可以联立方程组高斯消元,但是复杂度较高。也可以直接手动解方程,线性时间复杂度,方法如下。
我们根据题目可知 f n = 0 f_n=0 fn=0,我们采用倒推。
换元做差,令
s
[
i
]
=
f
[
i
]
−
f
[
i
+
1
]
(
2
)
s[i]=f[i]-f[i+1](2)
s[i]=f[i]−f[i+1](2),则可知答案为
∑
i
=
1
n
s
[
i
]
\sum\limits_{i=1}^ns[i]
i=1∑ns[i]。
把(2)带入(1)可以得到
s
[
i
]
=
(
1
+
(
1
−
p
i
)
∗
s
[
i
−
1
]
)
/
p
i
s[i]=(1+(1-p_i)*s[i-1])/p_i
s[i]=(1+(1−pi)∗s[i−1])/pi,从而得到了
s
i
s_i
si的递推关系,令
s
[
0
]
=
0
s[0]=0
s[0]=0,从第一项开始求即可。
#include <iostream>
using namespace std;
int main() {
int n;
scanf("%d", &n);
double s = 0, ans = 0;
for (int i = 1; i <= n; i++) {
double p;
scanf("%lf", &p);
s = (1+(1-p)*s)/p;
ans += s;
}
printf("%.3lf", ans);
return 0;
}