JYUACM新生训练周赛2

A

数学

题意

定义一种数据存储类型,范围在 \([-1024, 1023]\) 。给出一个数,询问这个数存储在这个类型中的值是多少。

思路

可以发现这个数据存储类型的循环长度是2048,将给出的数模2048,如果余数在 \([0, 1023]\) 直接输出,否则将余数减去2048。

代码

#include <iostream>

using namespace std;

int main()
{
    int n;
    cin >> n;
    
    while (n --)
    {
        int x;
        cin >> x;
        
        x %= 2048;
        
        if (x < 1024) cout << x << ' ';
        else cout << x - 2048 << ' ';
    }
    
    return 0;
}

B

数学、思维

题意

给出一个数 \(n\) ,询问 \(1 \sim n\) 内有几个满意的数字。
满意的数字:一个整数,该数有 \(m\) 个因子,从小到大排序后分别为 \(x_1, x_2, x_3, ... , x_m\) ,其中第 \(m\) 个因子能被第 \(\lfloor (1 + m)/2 \rfloor\) 个因子整除,\(x_m \% x_{\lfloor (1 + m)/2 \rfloor} = 0\)

思路

\(m\) 个因子就是本身,一定可以被第 \(\lfloor (1 + m)/2 \rfloor\) 个因子整除,所以全部都是满意的数字。

代码

#include <iostream>

using namespace std;

int main()
{
    int t;
    cin >> t;
    
    while (t --)
    {
        int x;
        cin >> x;
        cout << x << '\n';
    }
    return 0;
}

购物

枚举

题意

牛牛带着 \(n\) 元钱去超市买东西,超市一共只有两款商品,价格为 \(a\) 元的篮球和价格为 \(b\) 元的足球,牛牛想把手里的钱尽可能花光,请问牛牛最少能剩多少钱?

思路

枚举买了多少个篮球后剩下的钱都买足球后可以得到最少剩下的钱。

代码

#include <iostream>

using namespace std;

int main()
{
    int n, a, b;
    cin >> n >> a >> b;
    
    int res = 1e9;
    for (int i = 0; i * a <= n; i ++)
        res = min(res, (n - i * a) % b);
    
    cout << res;
    
    return 0;
}

雷矩阵

模拟

题意

给你一个初始的雷矩阵,需要完成扫雷矩阵。
扫雷矩阵的每一行每一列都是一个数字,每个数字的含义是与当前位置相邻的8个方向中,有多少个雷。

思路

枚举每个非雷的格子,查询它8个方向的格子上有多少个雷,再填到该格子上。

代码

#include <iostream>

using namespace std;

const int dx[] = {0, 1, 0, -1, -1, -1, 1, 1};
const int dy[] = {1, 0, -1, 0, -1, 1, 1, -1};
const int N = 1000 + 10;

int n, m;
char g[N][N];

int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            cin >> g[i][j];
    
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
        {
            if (g[i][j] == '*') continue;
            int res = 0;
            for (int k = 0; k < 8; k ++)
            {
                int x = i + dx[k], y = j + dy[k];
                if (x < 1 || y < 1 || x > n || y > m) continue;
                if (g[x][y] == '*') res ++;
            }
            g[i][j] = '0' + res;
        }
    
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m; j ++)
            cout << g[i][j];
        cout << '\n';
    }
    
    return 0;
}

排队

前缀和、二分、双指针

题意

当前小猫位于 \(n + 1\) 的位置上,它可以和前面距离它最近且数值大于它的猫互换位置。每分钟排第一的猫会离队,询问小猫最快多少分钟可以离队。

思路

首先,第 \(i\) 个位置的猫等待离队的时间是 \(i\) 分钟,小猫如果不换位置,那么就必须等待 \(n + 1\) 分钟后才可以离队。

其次,小猫的移动位置是固定的,每次移动到前面距离它最近且数值大于的它的位置上,移动一次花一分钟。

所以,最终小猫离队时的位置要么是最初的位置,要么就是一个数值大于它的位置上,且移动花费的时间要小于等于该位置等待离队的时间。

所以,可以从后向前处理数值大于小猫的个数,每个数值大于小猫的位置上判断是否是合法的离队时间,取一个最小的时间就是答案。


以上是前缀和的思维,接下来分析二分的解法

假设小猫在第 \(t\) 分钟时离队,那么小于 \(t\) 分钟时小猫都在排队,大于等于 \(t\) 分钟时小猫都已经离队,所以题目具有二分性。二分小猫最早离队的时间。

然后分析二分答案的判定,假设此时二分到的时间是 \(x\) ,它判定小猫已经离队的条件是小猫处于 \([1, x]\) 内最后一个数值大于它的位置且移动时间小于该位置的等待时间。

代码

#include <iostream>

using namespace std;

const int N = 2e5 + 10;

int n, m;
int a[N], s[N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    cin >> m;
    
    int res = n + 1, sum = 0;
    for (int i = n; i; i --)
        if (a[i] > m) {
            sum ++; // 移动花费的时间
            if (sum <= i) res = i; // 移动到该位置时间小于等于该位置等待到离队的时间就可以进行移动,取最小值
        }
        
    cout << res;
    
    return 0;
}

二分

#include <iostream>

using namespace std;

const int N = 2e5 + 10;

int n, m;
int a[N];

bool check(int x) {
    int pos = 0, sum = 0;
    for (int i = 1; i <= n; i ++)
        if (a[i] > m && i <= x) pos = i;
        else if (a[i] > m && i > x) sum ++;
    
    return sum + 1 <= pos;
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    cin >> m;
    
    int l = 1, r = n + 1;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    
    cout << l;
    
    return 0;
}
posted @ 2024-12-11 23:32  Natural_TLP  阅读(16)  评论(0)    收藏  举报