“蔚来杯“2022牛客暑期多校训练营6 ABM题解

A-Array

题目大意:
给出一个序列a1、a2 … an ,a的倒数和小于等于1/2,构造一个序列(首位相连),使得相邻的i之间不超过ai

思路:
只要a的倒数和小于1,就可以保证这个序列的存在的。因此将每个a取小于a的最大的2的次幂,可以保证改变后的a的倒数和小于1,然后将a排序,从小到达往序列中放数即可,当放置的位置有数字时就向后移动到空位置,因此都是2的次幂,因此一旦找到空位置后,之后每隔ai的位置都是空的,这也是将a取成2的次幂的原因。可以说这是一道非常巧妙的题。

AC代码:

#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
using namespace std;

const long long mod = 1e9 + 7;

long long ksm(long long base, long long power)
{
    long long result = 1;
    base %= mod;
    while (power)
    {
        if (power & 1)
            result = (result * base) % mod;
        power >>= 1;
        base = (base * base) % mod;
    }
    return result;
}

long long dp[14][150];

long long dfs(int s, int r)
{
    if (dp[s][r] != 0) return dp[s][r];
    if (s == r)
        dp[s][r] = (s + 1) / 2;
    else if (s == 1)
        dp[1][r] = (1 + (r - 3) * ksm(r, mod - 2) % mod * dfs(s, r - 1) % mod) % mod;
    else
        dp[s][r] = (1 + 3 * s * ksm(r, mod - 2) % mod * dfs(s - 2, r - 1) % mod + (r - 3 * s) * ksm(r, mod - 2) % mod * dfs(s, r - 1) % mod) % mod;
    return dp[s][r];
}

signed main()
{
    mem(dp, 0);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t, cnt;
    char str[50];
    map<int, int> m;
    cin >> t;
    for (int i = 1; i <= t; i++)
    {
        m.clear();
        cnt = 0;
        cin >> str;
        for (int j = 0; j < 26; j += 2)
            m[str[j] * 137 + str[j + 1]]++;
        for (auto it : m)
            if (it.second == 1) cnt++;
        cout << "Case #" << i << ": " << dfs(cnt, 123) << "\n";
    }

    return 0;
}

B-Eezie and Pie

题目大意:
有一棵树,每个结点向上能达到的距离为di , 对于每个点,求出能到达该点的点数。

思路:
类似前缀和的做法,初始时每个点的值为1,得到每个点不能到达的最近祖先,将其值减1。从下往上算出每个点的值,计算方法为该点自身的值加上所有子树的值。
找最大祖先如果用倍增的话,复杂度为O(nlogn),可能会被卡常数,另一种方法是在dfs时用一个数组来记录路径上的点,从而用O(n)的方法找到每个点的最大祖先。

AC代码:

#include <bits/stdc++.h>
const int N = 2e6 + 5;
using namespace std;

vector<int> g[N];
int path[N], d[N], ans[N], n, cnt = 0;

void dfs(int u, int f)
{
    path[++cnt] = u;    //将当前的点加入路径中
    if (d[u] + 1 < cnt) //不能到达的最近祖先减1
        ans[path[cnt - d[u] - 1]]--;
    for (auto v : g[u])
    {
        if (v == f) continue;
        dfs(v, u);
    }
    for (auto v : g[u])
    {
        if (v == f) continue;
        ans[u] += ans[v];
    }
    cnt--; // dfs结束,将该点弹出
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int u, v;
    cin >> n;
    for (int i = 1; i <= n; i++)
        ans[i] = 1;
    for (int i = 2; i <= n; i++)
    {
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for (int i = 1; i <= n; i++)
        cin >> d[i];
    dfs(1, 0);
    for (int i = 1; i <= n; i++)
        cout << ans[i] << ' ';
    return 0;
}

M-Z-Game on grid

题目大意:
在一个矩形棋盘上,每个位置可能为’A’、‘B’、'.‘其中之一,Alice和Bob轮流移动棋子,每次只能向右或向下移动棋子,当棋子移动到’A’上时Alice获胜,当棋子移动到’B’上时Bob获胜,当棋子不能被移动且位于’.'上时,平局。棋子初始位置在左上角,Alice先手,Bob走的每一步都是随机的,问Alice能否控制自己获胜、平局或是输掉。

思路:
可以把两人移动棋子的情况画成一棵树,以Alice获胜为例,那么无论Bob怎么走,棋子总是位于获胜的路径上。
当Alice移动时,子树中至少要有一棵位于获胜的路径上才行,当Bob移动时,所有的子树都必须位于获胜的路径上。

AC代码:

#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
const int N = 5e2 + 5;
using namespace std;

char g[N][N];
int n, m;
int sg[2][N][N]; // sg[0][i][j]表示棋子位于(i,j)上且是Alice移动的结果

// Alice赢,p为0表示为Alice在走,1表示Bob走
bool dfs1(int p, int i, int j)
{
    if (sg[p][i][j] != -1) return sg[p][i][j]; //已经有结果了,直接返回
    if (g[i][j] == 'A') return 1;   //赢了
    if (g[i][j] == 'B') return 0;   //输了
    if (i == n && j == m) return 0; //平局
    bool res;
    if (p == 0) // Alice走,有一条路径为获胜路径就行
    {
        res = 0;
        if (i + 1 <= n) res |= dfs1(p ^ 1, i + 1, j);
        if (j + 1 <= m) res |= dfs1(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    else // Bob走,所有的路径为获胜路径才行
    {
        res = 1;
        if (i + 1 <= n) res &= dfs1(p ^ 1, i + 1, j);
        if (j + 1 <= m) res &= dfs1(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    return sg[p][i][j];
}

//平局
bool dfs2(int p, int i, int j)
{
    if (sg[p][i][j] != -1) return sg[p][i][j];
    if (g[i][j] != '.')
        return 0;
    if (i == n && j == m && g[i][j] == '.') return 1;
    bool res;
    if (p == 0)
    {
        res = 0;
        if (i + 1 <= n) res |= dfs2(p ^ 1, i + 1, j);
        if (j + 1 <= m) res |= dfs2(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    else
    {
        res = 1;
        if (i + 1 <= n) res &= dfs2(p ^ 1, i + 1, j);
        if (j + 1 <= m) res &= dfs2(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    return sg[p][i][j];
}
// Bob赢
bool dfs3(int p, int i, int j)
{
    if (sg[p][i][j] != -1) return sg[p][i][j];
    if (g[i][j] == 'B') return 1;
    if (g[i][j] == 'A') return 0;
    if (i == n && j == m) return 0;
    bool res;
    if (p == 0) // A走
    {
        res = 0;
        if (i + 1 <= n) res |= dfs3(p ^ 1, i + 1, j);
        if (j + 1 <= m) res |= dfs3(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    else
    {
        res = 1;
        if (i + 1 <= n) res &= dfs3(p ^ 1, i + 1, j);
        if (j + 1 <= m) res &= dfs3(p ^ 1, i, j + 1);
        sg[p][i][j] = res;
    }
    return sg[p][i][j];
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> g[i] + 1;

    mem(sg, -1);
    if (dfs1(0, 1, 1))
        cout << "yes ";
    else
        cout << "no ";

    mem(sg, -1);
    if (dfs2(0, 1, 1))
        cout << "yes ";
    else
        cout << "no ";

    mem(sg, -1);
    if (dfs3(0, 1, 1))
        cout << "yes\n";
    else
        cout << "no\n";
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值