【ACM算法竞赛日常训练】DAY2题解与分析【比赛】【数学考试】【简单瞎搞题】
DAY2共三题:
-
比赛(概率)
-
数学考试(前缀和与思维)
-
简单瞎搞题(dp)
视频讲解:https://www.bilibili.com/video/BV1hP411o7RD/
🎈 作者:Eriktse
🎈 简介:19岁,211计算机在读,现役ACM银牌选手🏆力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)🚀
🎈 个人博客:www.eriktse.com
比赛
- tag: 概率
传送门:https://ac.nowcoder.com/acm/problem/14734
我们设对于每一道题:
-
事件A:自己做对了
-
事件B:听左边的,做对了
-
事件C:听右边的,做对了
三个事件的概率都已经给出了,分别是\(P(A), P(B), P(C)\)。
设事件Y:该题做对了
我们可以知道事件\(Y=A \cup B \cup C\),如果要正向求解\(Y\)是有一定难度的,因为这里的\(Y\)实际上由7个最小项构成,而事件\(\overline{Y}\)仅有一个最小项构成(这里就不展开说了)。
容易得到表达式:
然后我们可以枚举所有的序列,然后算一下当前这个情形对答案的贡献。
枚举序列可以用二进制数枚举,也可以用dfs,用dfs的话有个好处就是可以剪枝。
二进制枚举可能常数小点。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 20;
double a[maxn], b[maxn], c[maxn];
double ans[maxn];
double getp(int k)
{
return max(0.0, 1.0 - (1 - a[k]) * (1 - b[k]) * (1 - c[k]));
}
void dfs(int dep, int cnt, double p)
{
if(p == 0)return;
if(dep == 13)
{
ans[cnt] += p;
return;
}
//没对第i题
dfs(dep + 1, cnt, p * (1.0 - getp(dep)));
//对了第i题
dfs(dep + 1, cnt + 1, p * getp(dep));
}
signed main()
{
for(int i = 1;i <= 12; ++ i)scanf("%lf", a + i);
for(int i = 1;i <= 12; ++ i)scanf("%lf", b + i);
for(int i = 1;i <= 12; ++ i)scanf("%lf", c + i);
//for(int i = 1;i <= 12; ++ i)printf("p[%lld] = %.2lf\n", i, getp(i));
dfs(1, 0, 1);
for(int i = 0;i <= 12; ++ i)printf("%.6f\n", ans[i]);
return 0;
}
数学考试
- tag: 前缀和,思维
传送门:https://ac.nowcoder.com/acm/problem/15553
这道题比较简单,我们先处理出一个前缀和,然后从后往前去枚举左区间的左端点,然后更新答案即可。不需要知道具体的右区间,只需要知道右区间和取值的最值即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e5 + 9, inf = 8e18;
int a[maxn], prefix[maxn];
void solve()
{
int n, k;scanf("%lld %lld", &n, &k);
for(int i = 1;i <= n; ++ i)scanf("%lld", a + i);
for(int i = 1;i <= n; ++ i)prefix[i] = prefix[i - 1] + a[i];
int ans = -inf;
int mx = prefix[n] - prefix[n - k];
for(int i = n - 2 * k + 1;i >= 1; -- i)
{
//将[i, i + k - 1]区间作为左区间更新答案
ans = max(ans, mx + prefix[i + k - 1] - prefix[i - 1]);
//将区间[i + k - 1, i + 2 * k - 1]添加进右区间的集合中取大
mx = max(mx, prefix[i + 2 * k- 1] - prefix[i + k - 1]);
}
printf("%lld\n", ans);
}
signed main()
{
int _;scanf("%lld", &_);
while(_ --)solve();
return 0;
}
简单瞎搞题
- tag: dp,bitset
传送门:https://ac.nowcoder.com/acm/problem/17193
不会使用bitset的同学可以看这篇文章:https://www.eriktse.com/technology/1073.html
我们定义一个bitset b
,其中b[i]
若为1
表示可以组成平方和为i
的答案。
注意这里要用一个全0的新bitset s
来存下b
左移后的结果,然后再让b = s
,否则可能会出现大量的错误计算。
背包dp,具体看代码吧不难理解。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 9;
signed main()
{
int n;scanf("%lld", &n);
bitset<maxn> b;
b[0] = 1;
for(int i = 1;i <= n; ++ i)
{
int l, r;scanf("%lld %lld", &l, &r);
bitset<maxn> s;
for(int j = l;j <= r; ++ j)
{
s |= (b << (j * j));
}
b = s;
}
printf("%lld\n", b.count());
return 0;
}
🎈 本文由eriktse原创,创作不易,如果对您有帮助,欢迎小伙伴们点赞👍、收藏⭐、留言💬