2024牛客暑期多校训练营6
Abstract
好难qwq
A-Cake
Idea
全是博弈!首先来解释题目意思。
phase 1:给出一颗树,根节点为 1 ,树上每一条边的权值为 0 或者 1。初始时刻,根节点处有一只小马,小 G 和 小 O 依次控制小马移动,每次只能向子节点移动,若当前节点是叶节点,phase 1 结束。在移动的过程中,需要记录经过的边的权值,如此得到一个 01 串,记作 S 。
phase 2:假设 phase 1 得到的 01 串长度为 m ,那么小 O 将蛋糕分为 m 份(不一定等分!),然后从头遍历 S ,若当前位置的字符为 1 ,则小 G 选取一份蛋糕,否则,小 O 选取一份蛋糕。
现在,小 O 和小 G 都希望得到尽可能多的蛋糕,那么,最终小 G 能得到的比例为多少?
下面,我们来考虑小 O 会怎么分蛋糕,以 01 串 0111111 为例,小 O 先选蛋糕,那么我们把蛋糕全部集中在某一份上就可以了,小 G 得到的部分为 0% ;再举一个例子,01 串 11001111 ,此时小 O 只需要把蛋糕分配到前 4 个部分即可,因为后面 4 次全是小 G 拿蛋糕!为了使自己的利益最优,小 O 一定会将蛋糕平均分配到前 4 个部分!如果不均分的话比较大的部分肯定会被小 G 先拿走,亏了。为了叙述方便,我们把小 O 不给后面几份分蛋糕的行为称为截断。
搞清楚这些事情之后,就可以愉快地博弈了。实现细节见注释。
Code
#include <bits/stdc++.h>
using namespace std;
inline void read(int &x)
{
int f = 1;
char c = getchar();
for (x = 0; c < '0' || c > '9'; c = getchar())
if (c == '-')
f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + (c & 15);
x *= f;
}
struct Edge
{
int v, to, next;
} edge[600010];
int head[600010];
int cnt = 0;
void add(int x, int y, int k)
{
edge[++cnt].next = head[x];
edge[cnt].v = k;
edge[cnt].to = y;
head[x] = cnt;
return;
}
void init()
{
cnt = 0;
int n;
read(n);
for (int i = 0; i < n + 1; i++)
{
head[i] = 0;
}
for (int i = 1; i < n; i++)
{
int x, y, k;
read(x), read(y), read(k);
// 注意要存双向边,因为不知道谁是父节点
add(x, y, k);
add(y, x, k);
}
return;
}
// dfs0 返回的是当小 O 已经位于 pos 点时,他可以得到的最大利益
double dfs0(int pos, int sum1, int sum0, int fa);
// dfs1 返回的是当小 G 已经位于 pos 点时,他可以得到的最大利益
double dfs1(int pos, int sum1, int sum0, int fa)
{
double res = 0;
int cnt = 0; // cnt 记录当前节点拥有的子节点数目
for (int i = head[pos]; i; i = edge[i].next)
{
if (fa == edge[i].to) // 不能跑回父节点!
{
continue;
}
cnt++;
// 如果顺着这个子节点走下去,计算小 O 可以得到的最大利益,我们就可以反推出小 G 可以得到的最大利益
if (edge[i].v == 1)
{
res = max(1 - dfs0(edge[i].to, sum1 + 1, sum0, pos), res);
}
else
{
res = max(res, 1 - dfs0(edge[i].to, sum1, sum0 + 1, pos));
}
}
if (cnt == 0)
{
return double(sum1) / double(sum1 + sum0);
}
// 截断操作!
if (sum1 + sum0 > 0)
{
res = min(double(sum1) / double(sum1 + sum0), res);
}
return res;
}
double dfs0(int pos, int sum1, int sum0, int fa)
{
double res = 0;
int cnt = 0;
for (int i = head[pos]; i; i = edge[i].next)
{
if (fa == edge[i].to)
{
continue;
}
cnt++;
// 和 dfs1 同理
if (edge[i].v == 1)
{
res = max(res, 1 - dfs1(edge[i].to, sum1 + 1, sum0, pos));
}
else
{
res = max(res, 1 - dfs1(edge[i].to, sum1, sum0 + 1, pos));
}
}
if (cnt == 0)
{
return double(sum0) / double(sum1 + sum0);
}
// 截断操作
res = max(res, double(sum0) / double(sum1 + sum0));
return res;
}
void work()
{
printf("%.12lf\n", dfs1(1, 0, 0, -1));
return;
}
int main()
{
int t;
read(t);
while (t--)
{
init();
work();
}
return 0;
}
B-Cake 2
Idea
考虑每拉出一条新的弦,这条新弦可以切割多少条原有的弦,如果切割了 m 条,那么区域将增加 m + 1 个。但是!有一种情况是特殊的,当 n == 2 * k 时,这些弦共交点,需要另外讨论。
- n == 2 * k 时,直接输出 n 即可。
- n != 2 * k 时,首先, k = min ( k , n - k ),为什么这么做呢,你自己画图
模拟一下就知道了。然后,我们用序列 a 表示每增加一条新的弦,他切割了多少条弦,那么 a 序列的值就应该是 0 1 2 ... k-1...k-1 k k+1 ... 2k-1。为什么是这样呢,你模拟一下就知道了。然后,我们就可以推导出答案是 n + 1 + sum(a)。
Code
简洁不罗嗦。
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline void read(int &x)
{
int f = 1;
char c = getchar();
for (x = 0; c < '0' || c > '9'; c = getchar())
if (c == '-')
f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + (c & 15);
x *= f;
}
int n, k;
void init()
{
read(n), read(k);
return;
}
void work()
{
if (n % 2 == 0 && k * 2 == n)
{
cout << n;
return;
}
k = min(k, n - k);
cout << (n - 2 * k + 1) * (k - 1) + (k * (k - 1)) / 2 + (3 * k - 2) * (k - 1) / 2 + 1 + n;
return;
}
signed main()
{
init();
work();
return 0;
}
G-Genshin Impact's Fault
Idea
本场比赛的签到题,纯模拟。
给你一个序列,表示抽卡结果,要你判断这个结果是否符合原神的抽卡机制(玩原神最有用的一集)。
具体来说,满足以下条件的序列是合法的。
- 不能连续 10 个项都是 3 。
- 任意连续的 90 个项至少有一个 5 或者 U。
- 5 和 U 都是五星物品,若上一个出现的五星物品是 5 ,则下一次出现的五星物品必须是 U 。
Code
#include <bits/stdc++.h>
using namespace std;
inline void read(int &x)
{
int f = 1;
char c = getchar();
for (x = 0; c < '0' || c > '9'; c = getchar())
if (c == '-')
f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + (c & 15);
x *= f;
}
string text;
void init()
{
cin >> text;
return;
}
// 检测第一个条件是否满足,签到题命名就很随意了
bool shilianhefa()
{
int len = text.length();
int sum3 = 0;
int last4 = -1; // 上一次出现非 3 星物品的位置
int index = 0;
while (1)
{
index++;
if (last4 + index >= len) // 已经超出序列范围了
{
return 1;
}
if (text[index + last4] == '3') // 如果这次还是 3 星
{
sum3++;
if (sum3 >= 10) // 违反规则 1
{
return 0;
}
}
else
{
sum3 = 0; // 不是 3 星了,清空之前记录的 3 星的数量
last4 = index + last4;
index = 0;
}
}
return 1;
}
// 检测第二、三条件是否满足
bool chou90()
{
int len = text.length();
int last5 = -1; // 上一次出现五星的位置
int index = 0;
bool dabaodi = 0; // 是不是大保底(即上次已经出现 5 ,这次必为 U)
int sum5 = 0, sumU = 0;
while (1)
{
index++;
if (index + last5 >= len)
{
return 1;
}
if (text[index + last5] == 'U' || text[index + last5] == '5')
{
if (text[index + last5] == 'U')
{
dabaodi = 0;
sumU++;
}
else
{
if (dabaodi)
{
return 0;
}
dabaodi = 1;
sum5++;
}
last5 = last5 + index;
index = 0;
}
if (index >= 90)
{
return 0;
}
}
}
void work()
{
if (chou90() && shilianhefa())
{
cout << "valid" << endl;
}
else
{
cout << "invalid" << endl;
}
return;
}
int main()
{
int t;
read(t);
while (t--)
{
init();
work();
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战