首页 > 其他分享 >2023CSP-j复赛题解

2023CSP-j复赛题解

时间:2024-07-27 22:50:32浏览次数:15  
标签:2023CSP int 题解 time1 vis include 复赛 dis

csp-j题解

update :
2024.6.18 - 2024.6.25:重构题解

第一题:小苹果

原题洛谷P9748

思路

n 表示当前长度
求几天取完:每天取走 \((n - 1) / 3 + 1\) 个苹果,记录几天取完
第 \(n\) 个苹果第几天被取走:当 \(n \bmod 3 = 0\) 时被取走
时间复杂度约为 \(O(\log_n)\)

#include <iostream>
#include <cstdio>

using namespace std;

int n, tn = 1, day = 0, k;

int main()
{
    scanf("%d",&n);
    while (n){
        if ((n - 1) % 3 != 0 && k == 0) tn++;
        else k = 1;
        day++;
        n = n - (n - 1) / 3 - 1;
    }
    printf("%d %d", day, tn);
    return 0;
}

第二题:公路

原题洛谷P9749

思路

遍历每个油站,用当前到达的最小油价更新油费即可
因为从一个加油站到达下一个加油站不一定会将油消耗完,所以更新油费时应注意多出的油
时间复杂度为 \(O(n)\)

#include <iostream>
#include <cmath>

using namespace std;

const int N = 1e5 + 5;

long long n, d, v[N], a[N], w, x=N, tv, allv, gv;

int main(){
    scanf("%lld%lld", &n, &d);

    for(int i = 1;i < n; i++) 
        scanf("%lld", &v[i]), allv += v[i];

    for(int i = 1;i <= n; i++) 
        scanf("%lld", &a[i]);

    for(int i = 1, v1; i < n; i++)
    {
        gv += v[i];
        x = min(x, a[i]);
        v1 = ceil((gv - tv * d) * 1.0 / d);
        tv += v1;
        w += x * v1;
    }

    printf("%lld", w);
    return 0;
}

第三题:一元二次方程

原题洛谷P9750

分析

模拟题,模拟人类解一元二次方程即可

  1. 将二次项系数变为正数,放便处理
  2. 用 \(\Delta\) 判断根的情况
  3. 开根
int ti(int n){
    for (int i = 2; i * i <= n; i++)
        if (n % (i * i) == 0)
            return i * ti(n / (i * i)); 

    return 1;
}
  1. 约分,最大公约数用辗转相除法求最大公约数
// 辗转相除法
int gcd(int a,int b){
    return b ? gcd(b, a % b) : a;
}
  1. 按题意输出即可

代码如下

#include <iostream>
#include <algorithm>

using namespace std;

int t, m;

int ti(int n)
{
    for (int i = 2; i * i <= n; i++)
        if (n % (i * i) == 0) return i * ti(n / (i * i));
    return 1;
}

int gcd(int a, int b){ return b ? gcd(b, a % b) : a; }

void solve()
{
    int a, b, c, delta;
    cin >> a >> b >> c;
    if (a < 0) a = -a, b = -b, c = -c;
    delta = b * b - 4 * a * c;

    if (delta < 0) { cout << "NO" <<endl; return; }
    int k = ti(delta);

    if (k * k == delta)
    {
        int f = abs(gcd(2 * a, -b + k));
        cout << (-b + k) / f;
        if (2 * a / f != 1) cout << "/" << 2 * a / f;
        cout << endl;
        return;
    }

    if(delta == 0){
        int f = abs(gcd(2 * a, -b));
        cout << -b / f;
        if (2 * a / f != 1) cout << "/" << 2 * a / f;
        cout << endl;
        return;
    }

    int f = abs(gcd(-b, 2 * a));
    if(b == 0){
        f = abs(gcd(k, 2 * a));
        if (k / f != 1) cout << k / f << "*";
        cout << "sqrt(" << delta / (k * k) << ")";
        if (2 * a / f != 1) cout << "/" << 2 * a / f;
        cout << endl;
        return;
    }

    cout << -b / f;
    if (2 * a / f != 1) cout << "/" << 2 * a / f;
    cout << "+";
    f = abs(gcd(k, 2 * a));
    if (k / f != 1) cout << k / f << "*";
    cout << "sqrt(" << delta / (k * k) << ")";
    if(2 * a / f != 1) cout << "/" << 2 * a / f;
    cout << endl;
    return;
}
int main(){
    cin >> t >> m;
    while(t--){
        solve();
    }
    return 0;
}

第四题 旅游巴士

分析

几乎是最短路模版。

假设当前时间为 \(t\) 。

因为游客只有不早于 \(a_i\) 时刻才能通过第 \(i\) 道路。
所以

  1. 当 \(a_i > t\) 时,小Z不能通过这条路。
  2. 当 \(a_i <= t\) 时,小Z能通过这条路,通过这条路后时间变为 \(t+1\) 。

对于 \(t\) 时刻不能通行的道路,等待,直到能通过这条路

因为不能原地等待,所以只能在起点等待。

\(t\) 时刻与 \(a_i\) 时刻相差了 \(a_i-t\) 的时间,则小Z至少要在起点等待 \(a_x-t\) 的时间,因为到达和离开景区的时间都必须是 \(k\) 的非负整数倍,所以应在起点等待的时间为 \(\lceil \frac{a_x-t}{k} \rceil\times k\),到达 \(i\) 点的时间为\(\lceil \frac{a_x-t}{k} \rceil\times k+t\)

该题所求的答案并非最小值,而是能被 \(k\) 整除的最小值。
并且 \(k\) 并不大。
定义 \(dis_{i,j}\) 表示:到达\(i\)点的时间 \(t \bmod k\) 恰好 \(j\) 的值的最短时间
答案存在\(dis_{n,0}\)

代码如下:

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#define int long long
#define pi pair<int, int>

using namespace std;

const int N = 1e4 + 5, M = 105;

vector<pi> edge[N];
int n, m, k, dis[N][M], vis[N][M];
priority_queue<pi, vector<pi>, greater<pi>> q;

void add(int a, int b, int c){ edge[a].push_back({b, c}); }

void bian()
{
    for (int i = 1, u, v, a; i <= m; i++)
    {
        cin >> u >> v >> a;
        add(u, v, a);
    }
}

void dijkstra (int x)
{
    dis[x][0] = 0, q.push({0, x});

    while(q.size()){
        int u = q.top().second, p = q.top().first;
        q.pop();
        if (vis[u][p%k]) continue;
        vis[u][p % k] = 1;
        for(auto d : edge[u]){
            int v = d.first, w = d.second, time1;
            if(p >= w) time1 = p;
            else time1 = ((w - p - 1) / k + 1) * k + p;
            if (dis[v][(time1 + 1) % k] > time1 + 1)
            {
                dis[v][(time1 + 1) % k] = time1 + 1;
                q.push({time1 + 1, v});
            }
        }
    }
}

signed main()
{
    memset (dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    cin >> n >> m >> k;
    bian(), dijkstra(1);
    if(vis[n][0] == 0) cout << -1;
    else cout << dis[n][0];
    return 0;
}

结尾

终于写完了,新年礼物哈哈哈

标签:2023CSP,int,题解,time1,vis,include,复赛,dis
From: https://www.cnblogs.com/faruzan/p/18327653

相关文章

  • [ARC105C] Camels and Bridge 题解
    [ARC105C]CamelsandBridge题解出自梦熊比赛后,梦熊比赛出原题了,忘周知。也许更好的阅读体验思路全排列,差分约束,二分。全排列\(n\leq8\),且要指定顺序,易想到用全排列枚举所有状态。差分约束在全排列之后,需要求得每种状态的最短距离。定义所有骆驼的编号的集合为\(S\)......
  • ABC 364 F - Range Connect MST 题解
    一副扑克牌,去掉1到K,剩下就是我,赛后十秒过,我是joker。......
  • CF613E Puzzle Lover 题解
    Description给定一个\(2\timesn\)的矩阵,每个位置上有一个小写字母。有一个长度为\(k\)的小写字符串\(w\),询问矩阵中有多少条有向路径满足以下条件:路径上的字母连起来恰好为\(w\)。不多次经过同一个位置。只能向上下左右四个方向走。\(n,k\le2\times10^3\),答案......
  • IOI2022 邮局 加强版 题解
    [IOI2000]邮局加强版题解考虑动态规划,设\(f_{i,j}\)为经过了\(i\)个村庄,正在建第\(j\)​个邮局的最优距离。以及\(w_{i,j}\)表示区间\([i,j]\)​内建一个邮局时的距离总和。\(a\)数组表示每个村庄的坐标编号。朴素版状态转移方程:\[f_{i,j}=\min(f_{i,j},f_{k,j......
  • 07-25 题解
    07-25题解原题出处(按顺序):CF1556ECF1234FP9746CF1316EP3651CF17CCF1842HA转化:括号序列如果\(a_i\>b_i\),则有\(a_i-b_i\)个左括号如果\(a_i\<b_i\),则有\(b_i-a_i\)个右括号(第一个+1的位置一定在第一个-1的位置的左侧,所以情况一用左括号......
  • 第二次测试部分题解 (c,d,g)
    c-一个欧拉函数模板题 1#include<iostream>2usingnamespacestd;34intmain()5{6intn;7cin>>n;8intr=n;9for(inti=2;i*i<=n;i++)10{11if(n%i==0)12{13r......
  • 第二次测试部分题解
    A——暴力枚举计数就好了,可以参考这段代码#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#definemod100003#defineMAN1000100charstr[10];intans;voidok(){ ans=0; intlen=0; for(inti=0;str[i]!='\0';i++) len++; if(l......
  • ABC260F 题解
    题面根据题目描述,原图为二分图,设两侧点集为\(S,T\),大小为\(s,t(s\le3\times10^5,t\le3\times10^3)\)。注意到有四元环当且仅当\(T\)中存在一个点对\((a,b)\)同时和\(S\)中的某两个点连边。可以先考虑暴力,一种想法是:考虑枚举\(S\)中的点\(c\),设和\(c\)连边的点......
  • ABC263F 题解
    题面注意到把对局在图上表示出来是一颗满二叉树(叶节点为选手,其他点为对局),可以考虑树形dp。设\(x\)为\([l_x,r_x]\)之间选手的比赛,且该节点到叶子结点距离\(d_x\)。设\(f(x,p)\)表示胜者为\(p\)的最大钱数,有转移:\[\begin{aligned}f(x,p)&=f(lson(x),p)+g(rson(x))+a......
  • ABC262F 题解
    题面把“移动\(a_n\)至数列头”称为rotate,删除一项称为erase。因为要求字典序最小,所以可以逐位贪心。考虑一个数\(a_i\)怎么变成第一个数:使用\(n-i\)次rotate/erase,再rotate一次。删除或移动原来的\(a_{i+1}\sima_n\),再移动原来的\(a_i\)(逐步移动到数列尾,再ro......