【算法题解】2022河南萌新联赛第(三)场:河南大学

简介: 【算法题解】2022河南萌新联赛第(三)场:河南大学
比赛链接: https://ac.nowcoder.com/acm/contest/37782#question

墨染空大佬也来了,直接AK(orz)
在这里插入图片描述

A 玉米大炮

:tomato: 题目大意

链接: https://ac.nowcoder.com/acm/contest/37782/A
来源:牛客网

image-20220725205718478

:taco: 思路:二分答案

时间越长,击溃博士的概率越大。所以时间具有单调性,如果 x是击溃博士的最小时间,那么 x+1肯定也能击溃博士, x-1肯定无法击溃博士,所以大于 x的都能击溃,小于 x的都不能击溃,所以也具有 两段性,所以可以使用二分来做。这里我们需要注意一个细节,就是二分的左右边界,左边界最小是 0,因为第一发不需要时间。右边界是 1e18,假设只有一个大炮每次只能攻击 1生命值,装填时间是 1e9,而博士生命值是 1e9,所以最多需要 1e18时间。

:potato: 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, inf = 0x3f3f3f3f, mod = 998244353;
ll n, m;
ll a[N], b[N];
bool check(ll x)
{
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if(a[i] * (x / b[i]) + a[i]>=m)
        {
            return true;
        }
        ans += a[i] * (x / b[i]) + a[i];
        if (ans >= m)
        return true;
    }
    return false;
}
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
    }
    ll l = 0, r = 1e18;
    while (l < r)
    {
        ll mid = (l + r) >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << r;
    return;
}
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    int T = 1;
    while (T--)
        solve();
    return 0;
}

B 逆序对计数

:tomato: 题目大意

链接: https://ac.nowcoder.com/acm/contest/37782/B
来源:牛客网

image-20220725205928561

:taco: 思路:二分答案

一个区间改变了顺序,那么不影响区间外的逆序对个数,只影响区间内的个数,一个区间如果反转了,那么此区间的逆序对个数就变成了区间总对数减去原来区间内的总逆序对个数。每次询问相互独立,所以我们只需要记录每个区间的逆序对个数就可以解决问题了。考虑从 [l,r] ==> [l, r + 1]的逆序对个数增量,可以记录 [l, r]区间中必 a[r + 1]大的元素个数。统计完直接记录即可。当然也可以用树状数组求逆序对或者归并排序。

:potato: 代码

动态规划版

#include <bits/stdc++.h>
using namespace std;
const int N = 6e3 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int f[N][N]; // l到r地区间内的逆序对数量
int a[N];
int n, q;
void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = n; i >= 1; i--)
    {
        int cnt = 0;
        for (int j = i + 1; j <= n; j++)
        {
            f[i][j] = f[i + 1][j]; // [l,r]内的区间逆序对个数就等于[l + 1, r] 区间内的个数在加上有了l之后的个数
            if (a[i] > a[j])
                cnt++;//统计有了l后[l,r]区间内的逆序对增量
            f[i][j] += cnt;
        }
    }
    cin >> q;
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        int len = r - l + 1;
        int ans = f[1][n] - f[l][r] + (len - 1) * len / 2 - f[l][r];
        cout << ans << endl;
    }
}
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    int T = 1;
    while (T--)
        solve();
    return 0;
}

C.区间操作

:popcorn: 题目

链接: https://ac.nowcoder.com/acm/contest/37782/C
来源:牛客网

image-20220725211309005

:peach: 思路:线性筛 + 线段树

我们先来推导一下:

image-20220725212502656

这样我们就可以知道要求什么了。我们需要记录每个b[i]的所有质因数的指数的和。

每次都是求区间的和和修改区间,所以我们可以使用线段树,由于多次修改区间,所以还需要加懒标记。

这里还有一个问题,如果我们暴力求每个数漂亮值,时间复杂度O(根号x)那么一定会超时。x最大是4e6,所以我们只需要求出2000以内的所有质数就行(大约300个),那么那么求每一个数的指数个数的话就只需要处理300次即可。求质数我们可以使用线性筛。

:pear: 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int n, m;
int w[N];
bool st[N];
int primes[N];
int cnt1;
struct Node
{
    int l, r;
    ll sum, add;
} tr[N * 4];
void init()
{
    for (int i = 2; i <= 2e3; i++)
    {
        if (!st[i])
        {
            primes[cnt1++] = i;
        }
        for (int j = 0; primes[j] <= 2e3 / i; j++)
        {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0)
            {
                break;
            }
        }
    }
}
// 求一个数的漂亮值
int get(int d)
{
    int cnt = 0;
    for (int i = 0; i < cnt1; i++)
    {
        if (d % primes[i] == 0)
        {
            while (d % primes[i] == 0)
            {
                cnt++;
                d /= primes[i];
            }
        }
    }
    if(d > 1)
        cnt++;
    return cnt;
}
void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u)
{
    if (tr[u].add)
    {
        int d = tr[u].add;
        tr[u << 1].add += d;
        tr[u << 1].sum += d * (tr[u << 1].r - tr[u << 1].l + 1);
        tr[u << 1 | 1].add += d;
        tr[u << 1 | 1].sum += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * d;
        tr[u].add = 0;
    }
}
void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, l, get(w[l]), 0};
    }
    else
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}
void modify(int u, int l, int r, int d)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].sum += d * (tr[u].r - tr[u].l + 1);
        tr[u].add += d;
    }
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid)
            modify(u << 1, l, r, d);
        if (r > mid)
            modify(u << 1 | 1, l, r, d);
        pushup(u);
    }
}
ll query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r)
        return tr[u].sum;
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        ll sum = 0;
        if (l <= mid)
            sum += query(u << 1, l, r);
        if (r > mid)
            sum += query(u << 1 | 1, l, r);
        return sum;
    }
}
void solve()
{
    init();
    cin >> n;
    for (int i = 1; i <= n; i++)
        scanf("%d",&w[i]);
    build(1, 1, n);
    int q;
    cin >> q;
    while (q--)
    {
        ll k, l, r, s;
        cin>>k>>l>>r;
        if (k == 1)
        {
            cout << query(1, l, r) << endl;
        }
        else
        {
            cin>>s;
            s= get(s);
            modify(1, l, r, s);
        }
    }
}
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    int T = 1;
    while (T--)
        solve();
    return 0;
}

H 树上问题

:ear_of_rice: 题目

链接: https://ac.nowcoder.com/acm/contest/37782/H
来源:牛客网

image-20220726175012207

:ice_cream: 思路:树形DP

一个点的代价最大代价就等于从这个点到其他点的最长路径。我们需要求三个相连的点的最长路径和,那么首先我们就需要先求出每个点的最大代价。

那如何求一个点到其他点的最长路径呢?

一个点到其他点的最长路径无非是从它的几个子节点和它的父节点中寻找最长路径在加上自己的值。也就是向上走或者向下走的问题。

向下走是很容易的,我们只需要使用dfs寻找每一个点向下走的最长路径,然后在用子节点更新父节点。这里利用了回溯的特点。寻找向下走的最长距离还需要记录次长距离和分别是从哪个点下去的。具体为什么要求次长距离我们等一下在求向上走的时候需要用到。

image-20220726181425526

假设我们当前正在求2号点向下走的情况,可以遍历它的所有子节点56,在记录它的所有子节点的路径,保留最长路径和次长路径即可,通过一次dfs遍历我们就可以球的所有点向下走的最长路径。

我们再来看看向上走的情况,假设我们当前正在求5号点向上走的最长路径,那么它需要在2号点的其他子节点的最长路径以及2号点向上走的最长路径中找。如果我们一个一个找的话就回超时。这时我们在向下走时得到的最长距离和次长距离就排上了用场。

  • 5号点位于最长路径上,那么它向上走的距离就取2号点的子节点次长距离和2号点向上最长距离的最大值即可。这样我们的复杂度是O(1)的。

image-20220726183319977

  • 假设5号节点不在2号点的最长路径上,那么一定2好点的最长距离一定在它的其他子节点上。那5号点向上走的最长路径就等于2号点向上走的最长路径和2号点向下走的最长路径取最大值即可。

image-20220726183720499

求得每个点向上走和向下走的最长路径之后,它俩取最大值就是这个点到其他点的最长路径。

接下来就是求三个相连的点的最长路径的和的最大值了。如果求呢?

有两种情况:

  • 一个点和它的两个子节点组成的三个点的路径和最大
  • 一个点和它的儿子以及它的儿子的儿子组成的三个点的路径和最大

我们只需要遍历每个点,记录它的儿子中最大和次大的两个儿子,以及它儿子和它孙子的路径和,最后取最大值即可,如下图所示

image-20220726184410847

:jack_o_lantern: 代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10, M = N * 2;
int h[N], e[M], ne[M], idx;
int n, a[N];
int w[N];  //当前这个点到叶子节点的最长路径
int d1[N]; //当前这个点向下走的最长路径
int d2[N]; //当前这个点向下走的次长路径
int up[N]; //当前这个点向上早的最长路径
int p1[N]; //当前这个点的最长路径是从哪一个子节点下去的
int p2[N]; //当前这个点的次长路径是从哪一个子节点下去的
int ans;
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs_d(int u, int fa)
{
    d1[u] = d2[u] = a[u];
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa)
            continue;
        int d = dfs_d(j, u) + a[u];
        if (d >= d1[u]) //这里一定要大于等于
        {
            p2[u] = p1[u];
            d2[u] = d1[u];
            p1[u] = j;
            d1[u] = d;
        }
        else if (d > d2[u])
        {
            p2[u] = j;
            d2[u] = d;
        }
    }
    return d1[u];
}
void dfs_u(int u, int fa)
{
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa)
            continue;
        if (p1[u] == j)
            up[j] = max(up[u], d2[u]) + a[j];
        else
            up[j] = max(d1[u], up[u]) + a[j];
        dfs_u(j, u);
    }
}
int dfs(int u, int fa)
{
    int max1, max2, max3;
    max1 = max2 = max3 = 0;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa)
            continue;
        if (w[j] >= max1)
        {
            max2 = max1;
            max1 = w[j];
        }
        else if (w[j] > max2)
        {
            max2 = w[j];
        }
        max3 = max(max3, dfs(j, u));
        ans = max(ans, w[u] + w[j] + max3);
    }
    ans = max(ans, (w[u] + max1 + max2));
    return max1;
}
signed main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    // 求d1、d2、p1、p2数组
    dfs_d(1, -1);
    // 这里一定要初始化1的up数组
    up[1] = a[1];
    // 求 up数组
    dfs_u(1, -1);
    // 求w数组
    for (int i = 1; i <= n; i++)
        w[i] = max(d1[i], up[i]);
    // 计算总价值
    dfs(1, -1);
    cout << ans;
    return 0;
}

I 旅行

:star: 题目

链接: https://ac.nowcoder.com/acm/contest/37782/I
来源:牛客网

image-20220726102426667

:night_with_stars: 思路:分层图求最短路

每一个城市都有两种状态,一种是要做核酸,一种是不做。如果上一个城市做了核酸,那么当前这个城市就不用做,如果上个城市没做,则我当前一定要做。所以每个城市都有可能做核酸或者没做核酸,而后直接跑最短路即可

:yum: 代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
#define int long long
#define x first
#define y second
typedef priority_queue<int, vector<int>, less<int>> Q;
typedef pair<ll, pair<int, int>> PII;
const int N = 1e6 + 10, M = 1e6 + 10;
ll h[N], e[M], w[M], ne[M], idx;
ll dist[N][2];
bool st[N][2];
int n, m, x;
int ans;
void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1][0] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, {1, 0}});
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int ver = t.y.x;
        int state = t.y.y;
        ll d = t.x;
        if (st[ver][state])
            continue;
        st[ver][state] = true;
        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (state == 0)
            {
                if (dist[j][1] > dist[ver][0] + w[i] + x)
                {
                    dist[j][1] = dist[ver][0] + w[i] + x;
                    heap.push({dist[j][1], {j, 1}});
                }
            }
            else
            {
                if (dist[j][0] > dist[ver][1] + w[i])
                {
                    dist[j][0] = dist[ver][1] + w[i];
                    heap.push({dist[j][0], {j, 0}});
                }
            }
        }
    }
}

signed main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m >> x;
    while (m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }
    dijkstra();
    cout << min(dist[n][0],dist[n][1]);
    return 0;
}

J 神奇数字

:star: 题目

链接: https://ac.nowcoder.com/acm/contest/37782/J
来源:牛客网

image-20220726104121394

:night_with_stars: 思路:试除法求约数

image-20220726104810933

要想让两个数同余,那么它俩的差一定是x的倍数,三个数同余,那就是两两之间的差的倍数。一个数是几个数的约数,那么它一定是这几个数最大公约数的约数。

所以我们只需要求出最大公约数,然后在求最大公约数的约数即可。

:yum: 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, inf = 0x3f3f3f3f, mod = 998244353;
void solve()
{
    int a[3] = {0};
    cin >> a[0] >> a[1] >> a[2];
    sort(a, a + 3);
    if (a[0] == a[2])
    {
        puts("-1");
        return;
    }
    int x = __gcd((a[1] - a[0]), (a[2] - a[1]));
    vector<int> v;
    for (int i = 1; i <= x / i; i++)
    {
        if (x % i == 0)
        {
            v.push_back(i);
            if (i != x / i)
            {
                v.push_back(x / i);
            }
        }
    }
    sort(v.begin(), v.end());
    for (int i = 0; i < v.size(); i++)
        cout << v[i]<<" ";
    cout << endl;
}
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    int T = 1;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

:love_letter: 剩下的题慢慢补了,有任何疑问欢迎留言评论

相关文章
|
28天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
|
5天前
|
算法 数据安全/隐私保护 索引
OFDM系统PAPR算法的MATLAB仿真,对比SLM,PTS以及CAF,对比不同傅里叶变换长度
本项目展示了在MATLAB 2022a环境下,通过选择映射(SLM)与相位截断星座图(PTS)技术有效降低OFDM系统中PAPR的算法实现。包括无水印的算法运行效果预览、核心程序及详尽的中文注释,附带操作步骤视频,适合研究与教学使用。
|
13天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
14天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
15天前
|
存储 算法 决策智能
基于免疫算法的TSP问题求解matlab仿真
旅行商问题(TSP)是一个经典的组合优化问题,目标是寻找经过每个城市恰好一次并返回起点的最短回路。本文介绍了一种基于免疫算法(IA)的解决方案,该算法模拟生物免疫系统的运作机制,通过克隆选择、变异和免疫记忆等步骤,有效解决了TSP问题。程序使用MATLAB 2022a版本运行,展示了良好的优化效果。
|
14天前
|
机器学习/深度学习 算法 芯片
基于GSP工具箱的NILM算法matlab仿真
基于GSP工具箱的NILM算法Matlab仿真,利用图信号处理技术解析家庭或建筑内各电器的独立功耗。GSPBox通过图的节点、边和权重矩阵表示电气系统,实现对未知数据的有效分类。系统使用MATLAB2022a版本,通过滤波或分解技术从全局能耗信号中提取子设备的功耗信息。
|
14天前
|
机器学习/深度学习 算法 5G
基于MIMO系统的SDR-AltMin混合预编码算法matlab性能仿真
基于MIMO系统的SDR-AltMin混合预编码算法通过结合半定松弛和交替最小化技术,优化大规模MIMO系统的预编码矩阵,提高信号质量。Matlab 2022a仿真结果显示,该算法能有效提升系统性能并降低计算复杂度。核心程序包括预编码和接收矩阵的设计,以及不同信噪比下的性能评估。
34 3
|
25天前
|
人工智能 算法 数据安全/隐私保护
基于遗传优化的SVD水印嵌入提取算法matlab仿真
该算法基于遗传优化的SVD水印嵌入与提取技术,通过遗传算法优化水印嵌入参数,提高水印的鲁棒性和隐蔽性。在MATLAB2022a环境下测试,展示了优化前后的性能对比及不同干扰下的水印提取效果。核心程序实现了SVD分解、遗传算法流程及其参数优化,有效提升了水印技术的应用价值。
|
26天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真
本项目展示了基于贝叶斯优化(BO)的CNN-LSTM网络在数据分类中的应用。通过MATLAB 2022a实现,优化前后效果对比明显。核心代码附带中文注释和操作视频,涵盖BO、CNN、LSTM理论,特别是BO优化CNN-LSTM网络的batchsize和学习率,显著提升模型性能。
下一篇
无影云桌面