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 时,这些弦共交点,需要另外讨论。

  1. n == 2 * k 时,直接输出 n 即可。
  2. 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

本场比赛的签到题,纯模拟。

给你一个序列,表示抽卡结果,要你判断这个结果是否符合原神的抽卡机制(玩原神最有用的一集)。

具体来说,满足以下条件的序列是合法的。

  1. 不能连续 10 个项都是 3 。
  2. 任意连续的 90 个项至少有一个 5 或者 U。
  3. 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;
}
posted @   carboxylBase  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示