新生赛 F,H,J 题解

F. 金苹果

搜索题。

首先在输入中记录点 \(S,P,T\) 的坐标。

第一问,搜索要求不经过点 \(P\),可以将 px,py 记录成 #,然后从 \(S\) 开始搜(dfs 或 bfs 均可)能不能到达 \(T\)

第二问,从 \(S\) 出发能否先经过 \(P\) 再到达 \(T\),就相当于能否从 \(P\) 出发,既能到达\(S\)又能到达\(T\),两次搜索判断即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 50 + 7;
int n, m;
char s[inf][inf];
int dx[4] = {0, 0, 1, -1},
    dy[4] = {1, -1, 0, 0};
bool vis[inf][inf];
bool dfs(int x, int y, char ed)
{
    if (s[x][y] == ed)
    {
        return 1;
    }
    vis[x][y] = 1;
    bool ret = 0;
    for (int i = 0; i < 4; i++)
    {
        int xx = x + dx[i], yy = y + dy[i];
        if (xx < 1 || xx > n || yy < 1 || yy > m)
            continue;
        if (vis[xx][yy] || s[xx][yy] == '#')
            continue;
        ret = ret || dfs(xx, yy, ed);
        if (ret)
            return ret;
    }
    return ret;
}
struct node
{
    int x, y;
    node() {}
    node(int x, int y) : x(x), y(y) {}
};
void _()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> (s[i] + 1);
    }
    int sx, sy, px, py, tx, ty;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (s[i][j] == 'S')
                sx = i, sy = j;
            if (s[i][j] == 'P')
                px = i, py = j;
            if (s[i][j] == 'T')
                tx = i, ty = j;
        }
    }

    memset(vis, 0, sizeof(vis));
    s[px][py] = '#';
    cout << (dfs(sx, sy, 'T') ? "Yes" : "No") << ' ';
    s[px][py] = 'P';

    memset(vis, 0, sizeof(vis));
    bool rchS = dfs(px, py, 'S');
    memset(vis, 0, sizeof(vis));
    bool rchT = dfs(px, py, 'T');
    cout << ((rchS && rchT) ? "Yes" : "No") << '\n';
}
signed main()
{
    int qwq = 1;
    cin >> qwq;
    for (int i = 1; i <= qwq; i++)
        _();
    return 0;
}

H. 简单点

对于两个数之间的运算,第一反应是乘法得到的结果会比加法大。但是会有一种特殊情况,即两个运算数中存在 \(1\),此时 \(a + 1 > a * 1\)

于是我们将找出所有的 \(1\),进行加操作,找出所有 \(>1\) 的数,进行乘操作,然后两者结果相乘即可。

但是仔细观察会发现,如果只有一个 \(1\),那么这时候需要将这个 \(1\) 先和 \(>1\)最小数字 相加后再相乘,因为 \(a * b * c + 1 < a * (b + 1) * c <(a + 1) * b * c\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 1e5 + 7, mod = 998244353;
int n, sum1, ans;
vector<int> a;
signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int k;
        cin >> k;
        if (k == 1)
            sum1++;
        else
            a.push_back(k);
    }
    sort(a.begin(), a.end());
    if (sum1 == 1)
    {
        ans = sum1 + a[0];
        for (int i = 1; i < a.size(); i++)
            ans = ans * a[i] % mod;
    }
    else
    {
        ans = 1;
        if (sum1)
            a.push_back(sum1);
        for (int i = 0; i < a.size(); i++)
            ans = ans * a[i] % mod;
    }
    cout << ans << '\n';
    return 0;
}

由于取模的特性,在乘的过程中可能重新得到 \(1\),此时应该进行乘操作,但是如果直接判断是否有 \(1\),或取 max 则会导致出错。

此 hack 来自方少

\(96829702242 = 97 * 998244353 + 1\),可以分解为 \(96829702242 = 2 * 3 * 7 * 13 * 13 * 19 * 19 * 23 * 31 * 53\),则当程序运行到 \(* 53 \% 998244353\) 之后得到的结果为 \(1\),此时会出错。

J. 读书人

模拟题。

洛谷原题弱化版。

本题代码(有分步解析):

#include <bits/stdc++.h>
using namespace std;
char s[55], a[55], b[55];
char w[20] = " GSBQWSBQY";
int cnt, cnta, cntb;
bool pd_xsd, pd_top0;
bool pd_all0(int n)
{
    while (n > 0)
    {
        if (a[n--] != '0')
            return 0;
    }
    return 1;
}
bool pd_W(int n)
{
    while (n > 5)
    {
        if (a[--n] != '0')
            return 0;
    }
    return 1;
}
int main()
{
    cin >> s;
    int len = strlen(s);

    for (int i = 0; i < len; i++)
    {
        if (s[i] == '.')
        {
            pd_xsd = 1;
            break;
        }
    }

    int cnt = len - 1;
    if (pd_xsd)
    {
        while (s[cnt--] != '.')
            b[++cntb] = s[cnt + 1];
        while (cnt >= 0)
            a[++cnta] = s[cnt--];
    }
    else
    {
        for (int i = 0; i < len; i++)
            a[len - i] = s[i];
        cnta = len;
    }

    if (a[cnta] > '9' || a[cnta] < '0')
    {
        if (a[cnta] == '-')
            cout << 'F';
        a[cnta--] = 0;
    }
    while (cnta > 0 && a[cnta] == '0')
        a[cnta--] = 0;

    if (cnta == 0)
        cout << 0;
    else
    {
        while (cnta)
        {
            if (a[cnta] != '0')
            {
                pd_top0 = 1, cout << a[cnta];

                if (cnta != 1 && cnta != 5)
                    cout << w[cnta];
                if (4 < cnta && cnta < 9)
                {
                    if (pd_W(cnta))
                        cout << 'W';
                }
            }
            else if (pd_top0)
            {
                pd_top0 = 0;
                if (!pd_all0(cnta))
                {
                    cout << 0;
                }
            }
            cnta--;
        }
    }

    if (pd_xsd && s[len - 1] != '.')
        cout << 'D';
    for (int i = cntb; i > 0; i--)
        cout << b[i];
    return 0;
}

这题的难点主要在于整数部分的处理。

先看其他部分:


判断有无小数点:

	 for (int i = 0; i < len; i++)
    {
        if (s[i] == '.')
        {
            pd_xsd = 1;
            break;
        }
    }

将整数和小数部分分开,分别用 a,b 两个数组存下来:

    int cnt = len - 1;
    if (pd_xsd)
    {
        while (s[cnt--] != '.')
            b[++cntb] = s[cnt + 1];
        while (cnt >= 0)
            a[++cnta] = s[cnt--];
    }
    else
    {
        for (int i = 0; i < len; i++)
            a[len - i] = s[i];
        cnta = len;
    }

判断符号位,并去除前导 0。

    if (a[cnta] > '9' || a[cnta] < '0')
    {
        if (a[cnta] == '-')
            cout << 'F';
        a[cnta--] = 0;
    }
    while (cnta > 0 && a[cnta] == '0')
        a[cnta--] = 0;

整数部分先跳过,小数部分:

    if (pd_xsd && s[len - 1] != '.')
        cout << 'D';
    for (int i = cntb; i > 0; i--)
        cout << b[i];

最后看整数部分:

    if (cnta == 0)
    {
        cout << 0;
        // 如果没有整数部分(去除前导 0 导致的),则直接输出 0。
    }
    else
    {
        while (cnta)
        { // 处理每一位
            if (a[cnta] != '0')
            {
                pd_top0 = 1, cout << a[cnta];
                // pd_top0 代表判断是否是 0 而且是连续段中的第一个 0,用于连续 0 的输出。
                if (cnta != 1 && cnta != 5)
                    cout << w[cnta];
                // 除了第一位和第五位特殊,其他情况全部可以直接输出单位。
                if (4 < cnta && cnta < 9)
                { // 第 5 到 8 位的数字需要考虑是否输出 'W'。
                    if (pd_W(cnta))
                        cout << 'W';
                }
            }
            else if (pd_top0)
            { // 一段连续的 0,只需要其第一位进行输出,其他的不输出。
                pd_top0 = 0;
                if (!pd_all0(cnta))
                { // 特殊情况,如果当前位之后全是 0,则不需要输出,否则需要输出。
                    cout << 0;
                }
            }
            cnta--;
        }
    }

其中:

第 5 到 8 位的数字需要考虑是否输出 'W'

我们有代码:

bool pd_W(int n)
{
    while (n > 5)
    { // 若此时第 5 位到当前位还有非 0 的数字,则不需要输出 'W'
        if (a[--n] != '0')
            return 0;
    }
    return 1;
}

特殊情况,如果当前位之后全是 0,则不需要输出,否则需要输出。

我们有代码:

bool pd_all0(int n)
{
    while (n > 0)
    {//判断当前位到最低位是否全是 0
        if (a[n--] != '0')
            return 0;
    }
    return 1;
}
posted @ 2025-10-12 16:48  Zvelig1205  阅读(146)  评论(0)    收藏  举报