2023-CSPJ 题解

2023-CSPJ

T1. 小苹果

考虑第一问,注意到每次去走一些数字后会变成另一个子问题,且 n ′ ≤ 2 3 n n'\leq \frac{2}{3}n n32n,故暴力维护即可。

对于第二问,用同样的办法,暴力维护 n n n 的标号即可。

Code 100pts

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}

int main()
{
    // freopen("in.txt", "r", stdin);   //输入重定向,目录下的in.txt文件中读取
    // freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中

    int n = read();

    int cnt = 0,  res = 0;

    while (n)
    {
        cnt++;
        int k = (n - 1) % 3;
        if (res == 0 && k == 0)
            res = cnt;
        n = n - 1 - (n - 1) / 3; // 每次减少的数量。
    }
    cout << cnt << ' ' << res;

    return 0;
}

T2.公路

考虑贪心,对于每个加油站,找到下一个比他便宜的油站,每次在这个位置加尽可能少的油,使得能顺利开到到下一个比他便宜的油站,一路贪心下去就可以了。注意开long long

拓展一下: 题目中的车的油桶是无限的。 如果现限制油桶容积这需要考虑新的算法。 用单调栈。

Code 100pts

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 2e5 + 10;
int n, d;

LL v[N], a[N]; // v[i] 代表i-i+1 的距离, a[i] 代表第i个站点的油价a[i]

inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}

int main()
{
    // freopen("in.txt", "r", stdin);   //输入重定向,目录下的in.txt文件中读取
    // freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中
    n = read(), d = read();
    for (int i = 1; i < n; i++)
        cin >> v[i];
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    LL ans = 0;
    LL best = 1e6;
    LL dis = 0;
    for (int i = 1; i <= n; i++)
    {
        best = min(a[i], best); // 选择最优的油价前往下一个目的地。
        dis += v[i];            // 下个目的地还需要加多少油,因为前面一次加油肯定有剩下油。
        LL oil = (dis + d - 1) / d;         //确认下这次加油数量,上取整计算。
        ans += (dis + d - 1) / d * best;    //更新下答案。
        dis -= oil * d;                     // 注意我们需要更新下dis,
        // cout << best << ' ' << ans << endl; //
    }
    cout << ans << endl;
    return 0;
}

T3. 一元二次方程

根据题意模拟即可。取得50分比较容易。即符合条件C,但是拿满分比较难,需要注意比较多的细节代码。

Code 50pts

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int t, m;
inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}


void solve(int a, int b, int c)
{
    int delta = b * b - 4 * a * c;

    // 无根的情况先处理。
    if (delta < 0)
    {
        cout << "NO" << endl;
        return;
    }

    // 剩下有根的情况需要讨论啦。
    // 首先把a>0 的情况和a<0 的情况分开讨论。
    if (a > 0)
    {

        int res = (-b + (int)sqrt(delta)) / (2 * a);
        cout << res << endl;
        return;
    }
    else
    {
        int res = (-b - (int)sqrt(delta)) / (2 * a);
        cout << res << endl;
        return;
    }
}

int main()
{
    // freopen("in.txt", "r", stdin);   //输入重定向,目录下的in.txt文件中读取
    // freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中

    t = read(), m = read();
    while (t--)
    {
        int a = read(), b = read(), c = read();
        solve(a, b, c);
    }

    return 0;
}

Code 100pts

#include <bits/stdc++.h>
using namespace std;

// 输出一个有理数
void print(int fz, int fm)
{
    int g = abs(__gcd(fz, fm));
    fz /= g, fm /= g;
    if (fm == 1)
        printf("%d", fz);
    else
        printf("%d/%d", fz, fm);
}
int main()
{
    int _;
    scanf("%d%*d", &_);
    while (_--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        int d = b * b - 4 * a * c;
        if (d < 0)
            printf("NO");
        else
        {
            int x = 0;
            for (int i = 1; i * i <= d; i++)
            {
                if (d % (i * i) == 0)
                    x = i;
            }
            if (x * x == d)
            {
                int fz, fm;
                if (a > 0)
                    fz = -b + x, fm = 2 * a;
                else
                    fz = b + x, fm = -2 * a;
                print(fz, fm);
            }
            else
            {
                int fz, fm;
                if (a > 0)
                    fz = -b, fm = 2 * a;
                else
                    fz = b, fm = -2 * a;
                if (fz != 0)
                {
                    print(fz, fm);
                    putchar('+');
                }
                int r = d / (x * x);
                if (a > 0)
                    fz = x, fm = 2 * a;
                else
                    fz = x, fm = -2 * a;
                int g = __gcd(fz, fm);
                fz /= g, fm /= g;
                if (fm == 1)
                {
                    if (fz == 1)
                        printf("sqrt(%d)", r);
                    else
                        printf("%d*sqrt(%d)", fz, r);
                }
                else if (fz == 1)
                {
                    printf("sqrt(%d)/%d", r, fm);
                }
                else
                {
                    printf("%d*sqrt(%d)/%d", fz, r, fm);
                }
            }
        }
        puts("");
    }
    return 0;
}

T4. 旅游巴士

考虑拆点,把一个点 u u u 拆成 ( u , 0 ∼ k − 1 ) (u,0\sim k-1) (u,0k1),若原图存在一条 u → v u\to v uv 的边,那么新图上则有 ( u , x ) → ( v , ( x + 1 )   m o d   k ) (u,x)\to (v,(x+1)\bmod k) (u,x)(v,(x+1)modk)

那么问题转化成求 ( 1 , 0 ) → ( n , 0 ) (1,0)\to (n,0) (1,0)(n,0) 的最短路。

考虑边的限制怎么解决,注意到我们可以任选开局的时机,因此如果在一个位置被卡住了,可以通过不断加 k k k 直到满足边的时间限制,走回头路一定不优,这个过程用一次 dij 实现即可实现。

时间复杂度 O ( n k log ⁡ ( m k ) ) \mathcal{O}(nk\log (mk)) O(nklog(mk))

此外,注意到到达时间越晚,关于边的限制也就越松,因此可以拆点后二分到达时间然后倒着 bfs。

Code 100pts

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 10005;

vector<pair<int, int>> G[N];
int d[N][105];
int vis[N][105];
struct Node
{
    int u, i, d;
    bool operator<(const Node &rhs) const
    {
        return d > rhs.d;
    }
};
int main()
{
    int n, m, kk;
    scanf("%d%d%d", &n, &m, &kk);
    while (m--)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back({v, w});
    }
    memset(d, 0x3f, sizeof d);
    priority_queue<Node> q;
    q.push({1, 0, d[1][0] = 0});
    while (q.size())
    {
        int u = q.top().u, i = q.top().i;
        q.pop();
        if (vis[u][i])
            continue;
        vis[u][i] = 1;
        for (auto [v, w] : G[u])
        {
            int t = d[u][i], j = (i + 1) % kk;
            if (t < w)
                t += (w - t + kk - 1) / kk * kk;
            if (d[v][j] > t + 1)
                q.push({v, j, d[v][j] = t + 1});
        }
    }
    if (d[n][0] == INF)
        d[n][0] = -1;
    printf("%d\n", d[n][0]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值