2025牛客寒假算法基础集训营1

比赛链接

A茕茕孑立之影

题目理解是找一个质数与数组里面所有的数都互质,那我们找一个极大的质数就行了。 判断一下存不存在 1 ,如果不存在就输出 1e7 就行了。
注意一下 1e7 是double 型的,输出的时候要输出 int 型的,转化一下或者直接输出就行

int n; cin >> n;
        vector<int> a(n);
        for (int i= 0; i < n; i ++) cin >> a[i];
        sort(a.begin(), a.end());
        if (a[0] == 1) cout << -1 << "\n";
        else cout << 10000007 << "\n";

B 一气贯通之刃

首先明确一下简单路径是什么
简单路径是指没有重边和闭环的图, 顶点不重复。那我们通过录入一下无向图。然后判断一下每个点的度数就可以了。
如果度数 > 2 那么一定是会重复经历的,就不是简单路径。
如果度数 == 1 说明只有出度,没有入度,或者只有入度,没有出度,那只需要记录一下即可。

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

const int N = 100010;

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    vector<vector<int>> g(n + 1);
    for (int i = 0; i < n - 1; i ++)
    {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    bool flag = false;
    int ans1 = 0, ans2 = 0;
    for (int i = 1; i <= n; i ++)
    {
        if (g[i].size() > 2) flag = true;
        if (g[i].size() == 1) 
        {
            if (!ans1) ans1 = i;
            else if (!ans2) ans2 = i;
        }
    }
    if (flag)
    cout << -1;
    else
    cout << ans1 << " " << ans2;
    return 0;
}

D 双生双宿之决

排序然后暴力就行
要判断一下前后两个相同的数字相同不相同就行了
代码

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

constexpr int N = 1e6 + 10;
int a[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t; cin >> t;
    while (t --)
    {
        int n; cin >> n;
        int sum = 0;
        for (int i = 1; i <= n; i ++) 
        {
            cin >> a[i];
            sum += a[i];
        }
        if (n & 1) 
        {
            cout << "No" << "\n";
            continue;
        }
        sort(a + 1, a + 1 + n);
        bool flag1 = true, flag2 = true;
        for (int i = 1; i <= n / 2; i ++)
        {
            if (a[i] != a[1])
            flag1 = false;
        }
        for (int i = n / 2 + 1; i <= n; i ++)
        {
            if (a[n] != a[i])
            flag2 = false;
        }
        if (flag1 && flag2 && a[1] != a[n]) cout << "Yes" << "\n";
        else cout << "No" << "\n";
    }
    return 0;
}

E 双生双宿之错

中位数定理:所有数到中位数的距离和是最近的。
通过上面这个定理,我们可以容易的出来左侧右侧最近的答案。由于左右两个数不能一样,所以我们判断一下让两个数不一样就行了。
代码如下:

#include <bits/stdc++.h>
#define int long long

void solve()
{
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; i++)
        std::cin >> a[i];
    std::sort(a.begin(), a.end());
    int half = n / 2;
    int lh = a[half / 2], rh = a[half + half / 2];
    int ans = 1e18;
    int res;
    for (int i = lh - 1; i <= lh + 1; i++)
        for (int j = rh - 1; j <= rh + 1; j++)
        {
            if (i == j)
                continue;
            res = 0;
            for (int k = 0; k < half; k++)
                res += abs(a[k] - i);
            for (int k = n / 2; k < n; k++)
                res += abs(a[k] - j);
            ans = std::min(ans, res);
        }
    std::cout << ans << "\n";
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int _;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

G井然有序之衡

排序之后进行相减统计需要加的数量和减的数量,最后输出即可。

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

constexpr int N = 1e6 + 10;
int a[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    for (int i = 1; i <= n; i ++)
    {
        cin >> a[i];
    }
    sort(a + 1, a + 1 + n);
    int jia = 0, jian = 0;
    for (int i = 1; i <= n; i ++)
    {
        i - a[i] > 0 ? jia += i - a[i] : jian += i - a[i];
    }
    if (jia + jian == 0)
    {
        cout << jia;
    }
    else cout << -1;
    return 0;
}

H 井然有序之窗

要在范围里面,考虑贪心。先对左边界进行排序然后进行遍历,比如说第一个样例, 1 3 肯定是对应的 1 是最好的,剩下的 1 4 和 2 3 那就相当于是 2 4, 2 3 那这时候要建一个堆来进行排序,对右边界小的先进行 pop,然后记录一下。
做这种题要考虑贪心,是对左边界进行贪心还是有边界进行贪心,还是有其他的什么操作。
综上,比如说现在遍历到了 1 ,我们可以把左边界全为 1 的放到小根堆里面,然后进行 pop。

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
constexpr int N = 1e6 + 10;
struct node
{
    int l, r, pos; 
    int eend;
} a[N];

priority_queue<PII, vector<PII>,greater<PII>> q;

bool cmp(node a, node b)
{
    if (a.l != b.l)
    return a.l < b.l;
    return a.r < b.r;
}

struct node1
{
    int quj, id;
} ans[N];

bool cmp1(node1 a, node1 b)
{
    return a.quj < b.quj;
}
bool st[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    for (int i = 1; i <= n; i ++)
    {
        cin >> a[i].l >> a[i].r;
        a[i].pos = i;
    }
    sort(a + 1, a + 1 + n, cmp);
    int j = 1;
    for (int i = 1; i <= n; i ++)
    {
        for (; j <= n;)
        {
            if (a[j].l == i) 
            {
                q.push({a[j].r, a[j].pos});
                j ++;
            }
            else 
                break;
        }    
        if (q.empty())
        {
            cout << -1;
            return 0;
        }
        auto x = q.top();
        if (x.first < i || q.empty()) // 如果队列里面就没有包含 i 的并且堆里面是空的,就直接 -1
        {
            cout << -1; 
            return 0;
        }
        q.pop();
        ans[i].id = i; ans[i].quj = x.second;
    }

    sort(ans + 1, ans + 1 + n, cmp1);

    for (int i = 1; i <= n; i ++)
    cout << ans[i].id << " ";

    return 0;
}

硝基甲苯之袭

硝基甲苯之袭
这个题要知道异或的一个规律
** 由 a ^ b = c, 可知 a ^ c = b, b ^ c = a **
那么我们可以把原来的式子写成 ai ^ g == aj
设 g = gcd(ai, aj). 我们知道 g 一定是 ai 的因数,那么我们枚举 ai 的因数即可。出现结果之后让之前的元素和这个元素相乘即可。

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

constexpr int N = 1e6 + 10;
int cnt[N];
int a[N];

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

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    int ans = 0;
    for (int i = 1; i <= n; i ++)
    {
        int x; cin >> x;
        a[i] = x;
        cnt[x] ++; // 记录下 x 的个数
    }

    for (int i = 1; i <= n; i ++)
        for (int x = 1; x * x <= a[i]; x ++) // 寻找 a[i] 的因数
        {
            if (a[i] % x == 0)
            {
                if (gcd(a[i], a[i] ^ x) == x) ans += cnt[a[i] ^ x];
                if (x * x != a[i] && gcd((a[i] / x) ^ a[i], a[i] / x) == a[i] / x) ans += cnt[a[i] / x ^ a[i]]; 有两种情况,一起加上即可。
            }
        }
        
    cout << ans / 2; // 最后的答案会是原来的两倍,要除以 2 
    return 0;
}

M 数值膨胀之美

最小化数组的极差,那么首先想到的是最小值变成原来的 2 倍,那么这样就i会有问题。如果最小值变为原来的 2 倍,比次小值大了怎么办。所以这时候次小值也要变为原来的 2 倍。
那么我们要考虑区间的最小值。是一个经典的 RMQ 问题,我们可以用 ST 表来实现这个

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long

const int N = 1e5 + 10;
const int logN = 20;
int fa[N][logN + 1], fm[N][logN + 1];
int Logn[N];
PII a[N];

// 初始化Logn数组
void init()
{
    Logn[1] = 0;
    for (int i = 2; i < N; i++)
        Logn[i] = Logn[i / 2] + 1;
}

// 获取区间最大值
int getmax(int l, int r)
{
    int s = Logn[r - l + 1];
    return max(fa[l][s], fa[r - (1 << s) + 1][s]);
}

// 获取区间最小值
int getmin(int l, int r)
{
    int s = Logn[r - l + 1];
    return min(fm[l][s], fm[r - (1 << s) + 1][s]);
}

signed main()
{
    init();

    int n;
    cin >> n;

    // 输入处理并初始化fa和fm数组以及a数组
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        fa[i][0] = x;  // 初始时,每个点的最大值就是它本身
        fm[i][0] = x;  // 同样,初始时,每个点的最小值也是它本身
        a[i] = {x, i}; // 记录原数组元素及其索
    }

    // 对a数组按元素值进行排序
    sort(a + 1, a + 1 + n);

    // 构建ST表
    for (int j = 1; j <= logN; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
        {
            fa[i][j] = max(fa[i][j - 1], fa[i + (1 << (j - 1))][j - 1]); // 更新最大值ST表
            fm[i][j] = min(fm[i][j - 1], fm[i + (1 << (j - 1))][j - 1]); // 更新最小值ST表
        }

    int l = 1e9, r = 0;
    int ans = 1e9;

    for (int i = 1; i <= n; i++)
    {
        int u = a[i].second;
        l = min(u, l), r = max(r, u);
        int mmax = getmax(l, r) * 2;
        int mmin = getmin(l, r) * 2;

        int mmax1 = -1e9, mmax2 = -1e9, mmin1 = 1e9, mmin2 = 1e9;
        if (l > 1)
        {
            int l1 = 1, r1 = l - 1;
            mmax = max(mmax, getmax(l1, r1));
            mmin = min(mmin, getmin(l1, r1));
        }
        if (r < n)
        {
            int l2 = r + 1, r2 = n;
            mmax = max(mmax, getmax(l2, r2));
            mmin = min(getmin(l2, r2), mmin);
        }
        // cout << ans << "\n";
        ans = min((mmax - mmin), ans);
    }
    cout << ans;

    return 0;
}
### 关于2020年寒假算法基础集训营中的欧几里得算法 在2020年的寒假算法基础集训营中,确实存在涉及欧几里得算法的相关题目。具体来说,在第四场竞赛的第一题即为“A. 欧几里得”,该题目的核心在于利用扩展欧几里得定理来解决问题[^5]。 #### 扩展欧几里得算法简介 扩展欧几里得算法主要用于求解形如 ax + by = gcd(a, b) 的线性不定方程的一组特解(x,y),其中gcd表示最大公约数。此方法不仅能够计算两个整数的最大公因数,还能找到满足上述条件的具体系数x和y。 对于给定的数据范围较小的情况可以直接通过递归来实现;而对于较大数据则需考虑效率优化问题。下面给出了一段基于C++语言编写的用于解决此类问题的模板代码: ```cpp #include<bits/stdc++.h> #define int long long using namespace std; // 定义全局变量存储结果 int x, y; void ex_gcd(int a, int b){ if(b == 0){ x = 1; y = 0; return ; } ex_gcd(b, a % b); int tmp = x; x = y; y = tmp - (a / b) * y; } ``` 这段程序实现了经典的扩展欧几里得算法逻辑,并且可以作为处理类似问题的基础工具函数调用。 #### 实际应用案例分析 回到原题本身,“A. 欧几里得”的解答思路就是先预处理斐波那契数列前若干项数值存入数组`a[]`内以便快速查询,之后针对每一次询问直接输出对应位置处两相邻元素之和即可得出最终答案。这实际上巧妙运用到了广为人知的裴蜀定理——任意一对互质正整数都可由它们自身的倍数组合而成,而这里正是借助了这一性质简化了解决方案的设计过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值